@jmanuelcorral/openteam 0.23.0 → 0.24.1

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.
Files changed (63) hide show
  1. package/README.es.md +28 -8
  2. package/README.md +24 -7
  3. package/dist/certificates/graph-release-certificate.json +2 -2
  4. package/dist/certificates/graph-shadow-certificate.json +2 -2
  5. package/dist/cli/setupAdapters.d.ts +2 -2
  6. package/dist/cli/setupAdapters.d.ts.map +1 -1
  7. package/dist/cli.js +955 -380
  8. package/dist/commands/agents.d.ts +6 -4
  9. package/dist/commands/agents.d.ts.map +1 -1
  10. package/dist/commands/dispatch.d.ts +1 -1
  11. package/dist/commands/dispatch.d.ts.map +1 -1
  12. package/dist/commands/setup.d.ts +22 -33
  13. package/dist/commands/setup.d.ts.map +1 -1
  14. package/dist/index.d.ts.map +1 -1
  15. package/dist/index.js +1497 -334
  16. package/dist/local/lemonade.d.ts.map +1 -1
  17. package/dist/local/lemonadeRequestGuard.d.ts +11 -0
  18. package/dist/local/lemonadeRequestGuard.d.ts.map +1 -0
  19. package/dist/local/lemonadeResidency.d.ts +22 -0
  20. package/dist/local/lemonadeResidency.d.ts.map +1 -0
  21. package/dist/local/modelResidency.d.ts +4 -0
  22. package/dist/local/modelResidency.d.ts.map +1 -0
  23. package/dist/local/slotPool.d.ts +13 -7
  24. package/dist/local/slotPool.d.ts.map +1 -1
  25. package/dist/messages/executionSetup.d.ts +19 -2
  26. package/dist/messages/executionSetup.d.ts.map +1 -1
  27. package/dist/messages/modelResidency.d.ts +19 -0
  28. package/dist/messages/modelResidency.d.ts.map +1 -0
  29. package/dist/messages/orchestration.d.ts +1 -0
  30. package/dist/messages/orchestration.d.ts.map +1 -1
  31. package/dist/messages/reload.d.ts +21 -0
  32. package/dist/messages/reload.d.ts.map +1 -0
  33. package/dist/opencodeArtifacts/createRosterReload.d.ts +2 -0
  34. package/dist/opencodeArtifacts/createRosterReload.d.ts.map +1 -0
  35. package/dist/opencodeArtifacts/orchestratorAgent.d.ts.map +1 -1
  36. package/dist/opencodeArtifacts/slashCommand.d.ts.map +1 -1
  37. package/dist/opencodeArtifacts/teamPreparation.d.ts.map +1 -1
  38. package/dist/orchestrator/coordinator.d.ts.map +1 -1
  39. package/dist/orchestrator/roster.d.ts +0 -7
  40. package/dist/orchestrator/roster.d.ts.map +1 -1
  41. package/dist/orchestrator/runtimeSelection.d.ts +4 -3
  42. package/dist/orchestrator/runtimeSelection.d.ts.map +1 -1
  43. package/dist/orchestrator/sessionStatus.d.ts +12 -0
  44. package/dist/orchestrator/sessionStatus.d.ts.map +1 -0
  45. package/dist/orchestrator/subsessions.d.ts +2 -10
  46. package/dist/orchestrator/subsessions.d.ts.map +1 -1
  47. package/dist/plugin/availability.d.ts +4 -1
  48. package/dist/plugin/availability.d.ts.map +1 -1
  49. package/dist/plugin/hooks.d.ts +2 -0
  50. package/dist/plugin/hooks.d.ts.map +1 -1
  51. package/dist/plugin/memoryTool.d.ts +9 -3
  52. package/dist/plugin/memoryTool.d.ts.map +1 -1
  53. package/dist/plugin/modelResidency.d.ts +47 -0
  54. package/dist/plugin/modelResidency.d.ts.map +1 -0
  55. package/dist/plugin/reloadTool.d.ts +33 -0
  56. package/dist/plugin/reloadTool.d.ts.map +1 -0
  57. package/dist/router/types.d.ts +2 -0
  58. package/dist/router/types.d.ts.map +1 -1
  59. package/dist/telemetry/diagnostics.d.ts +1 -1
  60. package/dist/telemetry/diagnostics.d.ts.map +1 -1
  61. package/dist/telemetry/events.d.ts +24 -0
  62. package/dist/telemetry/events.d.ts.map +1 -1
  63. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -1654,6 +1654,28 @@ var ModelCapabilityProfileSchema = z5.object({
1654
1654
  availability: z5.enum(["available", "degraded", "unavailable"])
1655
1655
  }).strict();
1656
1656
 
1657
+ // src/messages/reload.ts
1658
+ var reloadMessages = {
1659
+ description: "Queue a project-instance reload only after the user explicitly answers the fresh /create_roster reload question affirmatively. General tool approval, YOLO and earlier project-description answers are not consent. End this turn after queuing; do not dispatch work. Cancel withdraws a pending request.",
1660
+ action: "Request a confirmed reload or cancel this session's queued request.",
1661
+ confirmed: "True only for an unambiguous affirmative human answer to the current reload question, after successful preparation and the full Agent/Role/Model and file summaries. This field records consent; it does not independently prove a human answered.",
1662
+ invalid: "Reload not queued: provide only action (request or cancel) and explicit boolean confirmed. No target directory or other arguments are accepted.",
1663
+ declined: "Reload not queued: explicit affirmative consent to the current reload question is required. No files or policies were changed.",
1664
+ denied: "Reload is restricted to the openteam primary in this plugin's project.",
1665
+ closed: "This reload controller is no longer active. Check the current opencode instance before requesting another reload; agent discovery has not been verified.",
1666
+ other: "A reload request is already pending for another session. This call does not replace or extend its confirmation.",
1667
+ authorizing: "Host permission for this project's reload is still pending. This call queues nothing and does not extend the confirmation deadline.",
1668
+ none: "There is no queued reload for this session to cancel.",
1669
+ "reload-queued": "Reload queued, not completed. Waiting up to 30 seconds for this tool and confirming turn to finish and for all project sessions to be idle. End this turn now; no project work or agent readiness is implied.",
1670
+ "reload-waiting": "Reload is still waiting: project sessions are busy or retrying. They will not be aborted; the original 30-second deadline is unchanged.",
1671
+ "reload-accepted": "opencode accepted the project-instance reload request. Agent discovery and readiness have not been verified; existing discovery safeguards still apply.",
1672
+ "reload-cancelled": "The pending reload was cancelled before disposal was requested. Prepared files, history and policies are unchanged.",
1673
+ "reload-timeout": "Reload was not requested: the 30-second idle deadline expired. A new explicit confirmation is required; nothing will retry automatically.",
1674
+ "reload-status-failed": "Reload was not requested: project activity was missing, malformed, unavailable or did not respond within 5 seconds. No idle state was assumed and no automatic retry is armed.",
1675
+ "reload-dispose-failed": "opencode did not confirm acceptance of the reload request. No automatic retry is armed; check the current instance before trying again. Agent readiness is unverified.",
1676
+ "reload-unconfirmed": "Reload acceptance is unconfirmed because the request timed out, was cancelled or the instance was disposed while it was in flight. The host may still finish it; no automatic retry is armed. Check the current instance and agent discovery."
1677
+ };
1678
+
1657
1679
  // src/telemetry/diagnostics.ts
1658
1680
  var DIAGNOSTIC_CODES = [
1659
1681
  "availability-refresh-failed",
@@ -1666,6 +1688,14 @@ var DIAGNOSTIC_CODES = [
1666
1688
  "opencode-server-url",
1667
1689
  "opencode-version-read-failed",
1668
1690
  "privacy-inert",
1691
+ "reload-queued",
1692
+ "reload-waiting",
1693
+ "reload-accepted",
1694
+ "reload-cancelled",
1695
+ "reload-timeout",
1696
+ "reload-status-failed",
1697
+ "reload-dispose-failed",
1698
+ "reload-unconfirmed",
1669
1699
  "soak-evidence-rejected",
1670
1700
  "telemetry-warning",
1671
1701
  "telemetry-write-failed",
@@ -1693,6 +1723,15 @@ function diagnosticDescription(code) {
1693
1723
  return "openteam could not read the live opencode version; graph gates fail closed when this happens.";
1694
1724
  case "privacy-inert":
1695
1725
  return routingMessages.privacyModeDeprecated;
1726
+ case "reload-queued":
1727
+ case "reload-waiting":
1728
+ case "reload-accepted":
1729
+ case "reload-cancelled":
1730
+ case "reload-timeout":
1731
+ case "reload-status-failed":
1732
+ case "reload-dispose-failed":
1733
+ case "reload-unconfirmed":
1734
+ return reloadMessages[code];
1696
1735
  case "soak-evidence-rejected":
1697
1736
  return "Graph soak evidence contained rejected observation lines; cutover gate evidence may be incomplete.";
1698
1737
  case "telemetry-warning":
@@ -3317,6 +3356,25 @@ var executionSetupMessages = {
3317
3356
  `),
3318
3357
  localOnlyTitle: "Local execution",
3319
3358
  localRequiredError: "local execution requires an enabled local runtime",
3359
+ lemonadeDetectedHint: (loaded, advertised) => `detected - ${loaded} confirmed loaded of ${advertised} advertised text model(s)`,
3360
+ lemonadeNoLoadedModels: "Lemonade has no text models verified as loaded and ready. Keep the intended model active in Lemonade and rerun setup; inactive or unknown models cannot be selected or entered manually.",
3361
+ lemonadeModelNotLoaded: (model) => `Lemonade model "${model}" is not positively verified as loaded and ready. Keep that exact text model active and rerun setup; no substitute was selected and no implicit loading is allowed.`,
3362
+ lemonadeProbeRequired: "Lemonade setup requires a fresh read-only residency probe before writing configuration. Use the CLI with its runtime probe enabled; manual model IDs cannot bypass this requirement.",
3363
+ lemonadeProbeFailed: "Lemonade residency could not be verified at the configured endpoint. Check connectivity and the read-only /models, /health, and /internal/aliases responses, then rerun setup with the intended text models already active.",
3364
+ lemonadeEndpointMismatch: "Lemonade endpoint/provider mapping differs from the selected runtime. Reconcile the configured base URL and provider before rerunning setup; no model was substituted.",
3365
+ lemonadeModelIdentityUnknown: (model) => `Lemonade model "${model}" has no usable registered wire identity in the effective opencode configuration. Fix its model declaration before rerunning setup; setup will not replace or guess an alias.`,
3366
+ lemonadeEffectiveConfigLabel: "effective setup configuration",
3367
+ lemonadeEffectiveConfigAmbiguous: "Cannot establish one effective openteam configuration from the configured plugin loaders. Reconcile their options before rerunning setup; no provider identity was guessed.",
3368
+ lemonadeRetainedProviderUnprotected: (providerID) => `Cannot safely keep retained Lemonade provider "${providerID}": the effective configuration must retain one matching Lemonade runtime, disabled when deselected. Reconcile the plugin options or machine overlay, or explicitly remove the provider registration before rerunning setup; no configuration was written.`,
3369
+ lemonadeV2InvalidProvider: (providerID) => `Lemonade provider ${JSON.stringify(providerID)} cannot be represented as an exact native V2 policy resource. Choose a non-empty provider ID without wildcards, slashes, backslashes, or whitespace; setup will not broaden a deny rule.`,
3370
+ lemonadeV2InvalidPolicies: "Cannot safely preserve experimental.policies: experimental must be an object and policies must be an array of provider.use statements with a string resource and allow/deny effect. Fix the project configuration before rerunning setup.",
3371
+ lemonadeV2Title: "Lemonade transport compatibility",
3372
+ lemonadeV2Restriction: (providers) => `Project policies disable native V2 Lemonade access for: ${providers.join(", ")}. Ordinary guarded V1 configuration remains supported; V2 is not an openteam-hook execution path. Higher-precedence or global policies can override this project restriction; it is not a server-atomic no-autoload guarantee.`,
3373
+ lemonadeInventory: (instanceID, providerID, models) => {
3374
+ const inventory = models.map((model) => `${model.modelID} (${model.loaded === true ? "loaded" : model.loaded === false ? "inactive" : "residency unknown"})`).join(", ");
3375
+ return `Lemonade inventory ${instanceID} (provider ${providerID}): ${inventory || "no advertised text models"}. Only confirmed loaded text models count as ready.`;
3376
+ },
3377
+ lemonadeAgentResidency: (agent, model, state) => `Lemonade readiness: ${agent} (${model}): ${state === "not-loaded" ? "inactive (not ready)" : "residency unknown"}; advertised inventory is not loaded readiness. Keep this exact model active and verify residency before use; requests without verified readiness are blocked.`,
3320
3378
  mixedPrimaryQuestion: "Primary coordinator model",
3321
3379
  mixedPrimaryLocal: (model) => `Use local thinking model ${model}`,
3322
3380
  mixedPrimaryFrontier: "Choose a frontier model",
@@ -3339,12 +3397,14 @@ var executionSetupMessages = {
3339
3397
  `Local runtimes: ${params.localRuntimes}`,
3340
3398
  `YOLO mode: ${params.yolo ? "enabled (auto-approves permissions)" : "disabled"}`,
3341
3399
  "Web Console: launched separately with 'openteam console' (multi-session, loopback)",
3342
- `Wrote: ${params.opencodePath}, ${params.configPath}, ${params.agentPath}, ${params.agentDir}/*.md (${params.roleAgentCount} standard subagents), ${params.commandDir}/*.md (${params.commandCount} commands); ensured ${params.gitignorePath}`,
3400
+ `Wrote: ${params.opencodePath}, ${params.configPath}, ${params.agentPath}, ${params.commandDir}/*.md (${params.commandCount} commands); ensured ${params.gitignorePath}`,
3401
+ "Worker files are created through conversational roster preparation, not setup; existing worker files were left untouched.",
3343
3402
  "",
3344
3403
  "Next steps:",
3345
3404
  params.executionMode === "local" ? " 1. No frontier authentication is required while local execution is active" : " 1. Authenticate the frontier provider: opencode auth login",
3346
3405
  params.executionMode === "local" ? executionSetupMessages.localRuntimeNextStep : params.executionMode === "mixed" ? executionSetupMessages.mixedNextStep : executionSetupMessages.frontierNextStep,
3347
- " 3. Press Tab and pick the 'openteam' agent, or type / and pick an /openteam… command"
3406
+ " 3. Press Tab and pick the 'openteam' agent, or type / and pick an /openteam… command",
3407
+ " 4. Use /create_roster to compose the team without starting project work, or describe a task to prepare the team and begin work"
3348
3408
  ],
3349
3409
  configuredTitle: "openteam configured",
3350
3410
  complete: (paths) => `openteam setup complete: ${paths}`,
@@ -9207,7 +9267,7 @@ function parseOpencodeProviders(opencodeConfig) {
9207
9267
  function localRuntimeStates(config) {
9208
9268
  const map = new Map;
9209
9269
  for (const runtime of config.local.runtimes) {
9210
- map.set(runtime.id, { enabled: runtime.enabled });
9270
+ map.set(runtime.defaultModel.providerID, { enabled: runtime.enabled });
9211
9271
  }
9212
9272
  return map;
9213
9273
  }
@@ -9215,7 +9275,7 @@ function disabledLocalProviderIDs(config) {
9215
9275
  const ids = new Set;
9216
9276
  for (const runtime of config.local.runtimes) {
9217
9277
  if (!runtime.enabled) {
9218
- ids.add(runtime.id);
9278
+ ids.add(runtime.defaultModel.providerID);
9219
9279
  }
9220
9280
  }
9221
9281
  return ids;
@@ -9231,7 +9291,11 @@ function computeLive(model, isLocalProvider, liveModels) {
9231
9291
  if (!snapshot.reachable) {
9232
9292
  return "unreachable";
9233
9293
  }
9234
- return snapshot.modelIDs.has(model.modelID) ? "confirmed" : "not-advertised";
9294
+ if (snapshot.modelIDs.has(model.modelID))
9295
+ return "confirmed";
9296
+ if (snapshot.residency?.has(model.modelID))
9297
+ return snapshot.residency.get(model.modelID) === false ? "not-loaded" : "residency-unknown";
9298
+ return "not-advertised";
9235
9299
  }
9236
9300
  function resolveAgentModel(model, context) {
9237
9301
  const isLocalProvider = KNOWN_LOCAL_PROVIDER_IDS.has(model.providerID) || context.localRuntimes.has(model.providerID);
@@ -14568,6 +14632,48 @@ function withDeterministicGitLocale(env) {
14568
14632
  };
14569
14633
  }
14570
14634
 
14635
+ // src/opencodeArtifacts/createRosterReload.ts
14636
+ var CREATE_ROSTER_RELOAD_INSTRUCTIONS = [
14637
+ "## Reload confirmation — /create_roster only",
14638
+ "",
14639
+ "Only after successful persisted registration and verified agent files may you offer reload:",
14640
+ "read back the parseable persisted roster, including guaranteed roles; actually create each missing",
14641
+ "subagent file and verify every required existing or created file.",
14642
+ "First show the complete Agent/Role/Model table returned by `openteam-register-cast`,",
14643
+ "including primary, guaranteed and preserved agents with their unchanged tool-returned policy values.",
14644
+ "Then show the created-versus-preserved file summary and any warnings. Both must be visible",
14645
+ "BEFORE asking a fresh explicit reload question in this opencode conversation.",
14646
+ "If registration, file creation or verification failed or is partial, do not ask the reload question:",
14647
+ "report the exact blocker and do not claim readiness. If registration succeeded, still show its",
14648
+ "complete table even when file preparation later fails.",
14649
+ "",
14650
+ "Ask whether to reload this project's opencode agent definitions now, explaining the request is",
14651
+ "queued/waiting until the requesting turn completes and all project sessions are idle.",
14652
+ "Use the host question tool when available; otherwise ask a normal conversational question",
14653
+ "in opencode and WAIT for the user's answer. Never use a terminal wizard, tool permission",
14654
+ "approval or YOLO permission as reload consent. A greenfield project-description reply is NOT reload consent.",
14655
+ "Only an explicit affirmative answer to this specific reload question authorizes",
14656
+ '`openteam-reload` with `{ action: "request", confirmed: true }`.',
14657
+ "False, no, cancel, an ambiguous answer or no reply means no reload request.",
14658
+ "A decline before anything is queued needs no tool call; do not turn ambiguity into confirmation.",
14659
+ "To cancel an already queued request at the user's direction, use",
14660
+ '`openteam-reload` with `{ action: "cancel", confirmed: false }` and report its actual result.',
14661
+ "",
14662
+ "This internal tool is restricted to the product primary `openteam` and its bound project.",
14663
+ "Do not supply a directory, project, session or other model-selected target.",
14664
+ "Report queued/waiting honestly and end the confirming turn immediately.",
14665
+ "Do not await your own idle state, poll or hold the turn open. Do not start work or call",
14666
+ "`task` or `openteam-orchestrate`; do not rewrite/recast files or change models, permissions",
14667
+ "or execution policies. The idle wait has a finite timeout; surface timeout and failure results",
14668
+ "when the runtime reports them, including rejected requests, rather than promising success or silently retrying.",
14669
+ "SDK acceptance is not verified discovery or readiness. Preserve all existing discovery guards:",
14670
+ "if new agents remain undiscovered, surface the reload/discovery blocker;",
14671
+ "do not dispatch through a generic substitute or claim readiness.",
14672
+ "This workflow and all its replies remain composition only. After completion, decline or cancellation,",
14673
+ "wait for a separate work request; neither a reload answer nor queued acceptance starts project work."
14674
+ ].join(`
14675
+ `);
14676
+
14571
14677
  // src/opencodeArtifacts/rosterSummary.ts
14572
14678
  var ROSTER_SUMMARY_INSTRUCTIONS = [
14573
14679
  "After every successful cast creation, repair or extension, show the complete Agent/Role/Model table",
@@ -14600,6 +14706,20 @@ var TEAM_PREPARATION_INSTRUCTIONS = [
14600
14706
  "machine bindings stay unchanged; do not require a terminal or manually edit policy to finish hiring.",
14601
14707
  "Never infer execution domains from prose, relax permissions, or invent a concrete auto model.",
14602
14708
  "Registration/review is idempotent: a capable configured team needs no extra members or file rewrites.",
14709
+ "`agentName`, not `roleID`, determines each worker filename.",
14710
+ "Create `.opencode/agent/<agentName>.md` only for registered roster entries.",
14711
+ "A built-in role profile is not an extra agent.",
14712
+ "Do not create a parallel `<roleID>.md` when the roster maps that role to another alias.",
14713
+ "An existing roster alias equal to its roleID is still valid; do not rename it.",
14714
+ "Do not run `openteam setup` to hire workers or repair their files.",
14715
+ "Preserve unrelated and legacy generic files; do not delete, overwrite or silently register them.",
14716
+ "Registration writes roster/profile state, not worker Markdown.",
14717
+ "Report only verified file changes; do not call pre-existing files newly created.",
14718
+ "When creating a scribe worker, put the following memory instructions in its charter:",
14719
+ "Use `openteam-memory` for durable, reusable facts, entities and typed relations.",
14720
+ "Send only redacted knowledge messages, never raw conversations, prompts or credentials.",
14721
+ "Honor operator permissions and memory settings; do not broaden them.",
14722
+ "A disabled-memory or nothing-recorded result is expected; do not retry it.",
14603
14723
  "Create only missing agent definitions; preserve customized files. Show the registration table",
14604
14724
  "and verify preparation before dispatch. If opencode cannot discover a new agent, surface",
14605
14725
  "the reload/discovery blocker; do not dispatch through a generic substitute or claim readiness."
@@ -14641,33 +14761,43 @@ function buildOrchestratorAgent(model, options = {}) {
14641
14761
  "primary agent: there is no pre-created team. You **build the team of",
14642
14762
  "subagents on demand** according to the goal of the user and the repository.",
14643
14763
  "",
14644
- "## Golden rule: check the team on EVERY request",
14645
- "",
14646
- "Before answering or delegating **any** request, your **first action",
14647
- "always** is to check whether a team already exists, even if the user does not",
14648
- "explicitly ask you to create it. Never assume there is a team or that there is not:",
14649
- `verify it by looking at \`${OPENTEAM_ROSTER_PATH}\` and the \`.opencode/agent/*.md\``,
14650
- "files with `mode: subagent` (use `read`/`glob`/`list`).",
14651
- "",
14652
- ROSTER_SUMMARY_INSTRUCTIONS,
14653
- "",
14654
- TEAM_PREPARATION_INSTRUCTIONS,
14655
- "",
14656
14764
  "## Roster composition takes precedence",
14657
14765
  "",
14658
14766
  "For `/create_roster`, including replies to its pending questions, follow",
14659
14767
  "the command's roster-only workflow: do not distribute tasks or start implementation.",
14768
+ "These rules override the ordinary golden rule and capability checks below.",
14769
+ "A bare yes to a pending reload question continues only reload confirmation:",
14770
+ "do not restart roster reads, capability preparation or registration.",
14660
14771
  "Reuse or conservatively extend the existing cast and preserve customized agent files.",
14661
14772
  "Use a supplied description; otherwise inspect real project source read-only.",
14662
14773
  "If there is no meaningful source and no description, ask what the user wants to",
14663
14774
  "build in this conversation and wait before any writes or cast registration.",
14664
14775
  "Register through `openteam-register-cast`, create missing subagent files, verify",
14665
- "the persisted roster and files, then stop. Do not call `openteam-orchestrate` or",
14776
+ "the persisted roster and files, then follow the reload confirmation below.",
14777
+ "Do not call `openteam-orchestrate` or",
14666
14778
  "`task` for this workflow. A later answer to your question continues composition",
14667
14779
  "only until it completes or the user cancels; it is not an implementation request.",
14668
14780
  "",
14781
+ CREATE_ROSTER_RELOAD_INSTRUCTIONS,
14782
+ "",
14783
+ "## Golden rule: check the team on EVERY request",
14784
+ "",
14785
+ "The roster composition and pending reload-reply rules above take precedence.",
14786
+ "Before answering or delegating **any ordinary work** request, your **first action",
14787
+ "always** is to check whether a team already exists, even if the user does not",
14788
+ "explicitly ask you to create it. Never assume there is a team or that there is not:",
14789
+ `verify it by looking at \`${OPENTEAM_ROSTER_PATH}\` and the \`.opencode/agent/*.md\``,
14790
+ "files with `mode: subagent` (use `read`/`glob`/`list`).",
14791
+ "",
14792
+ ROSTER_SUMMARY_INSTRUCTIONS,
14793
+ "",
14794
+ TEAM_PREPARATION_INSTRUCTIONS,
14795
+ "",
14669
14796
  "## For work requests, including analysis, review and documentation",
14670
14797
  "",
14798
+ "Ordinary automatic hiring must NOT ask for reload or automatically reset the project.",
14799
+ "If discovery is blocked, surface the blocker and preserve the discovery guards instead.",
14800
+ "",
14671
14801
  "- **If the team ALREADY exists** → review capability coverage, reuse suitable",
14672
14802
  " members and extend only for uncovered needs. Complete any existing member's",
14673
14803
  " profile through `openteam-register-cast`; do not hire for a configuration gap.",
@@ -15042,8 +15172,10 @@ function buildCreateRosterCommand() {
15042
15172
  "required agent file exists. If anything failed, report the incomplete state and exact",
15043
15173
  "blocker; do not delete preserved state or claim the team is ready.",
15044
15174
  "Summarize the cast, role rationale/evidence, preserved versus newly created files, and",
15045
- "any warnings. New subagents may require an opencode reload before discovery.",
15046
- "Stop after composition; wait for a separate request before starting project work.",
15175
+ "any warnings. Continue only to the reload confirmation below, including its pending replies;",
15176
+ "do not restart preparation when the user answers that reload question.",
15177
+ "",
15178
+ CREATE_ROSTER_RELOAD_INSTRUCTIONS,
15047
15179
  "",
15048
15180
  "## Project description (user input)",
15049
15181
  "",
@@ -15903,6 +16035,243 @@ function ensureFoundryV1BaseURL(endpoint) {
15903
16035
  }
15904
16036
  }
15905
16037
 
16038
+ // src/local/lemonadeResidency.ts
16039
+ import { z as z22 } from "zod";
16040
+
16041
+ // src/messages/modelResidency.ts
16042
+ var reasons = {
16043
+ unknown: "fresh residency metadata is unavailable, malformed, or ambiguous",
16044
+ inactive: "the requested model is not verified as resident and serving",
16045
+ identity: "the requested model identity can be rewritten or is ambiguous",
16046
+ destination: "the request destination does not match its configured runtime",
16047
+ transport: "this provider execution path cannot enforce the residency guard",
16048
+ request: "this is not a supported, explicit model inference request",
16049
+ cancelled: "the request was cancelled before residency could be verified",
16050
+ timeout: "the residency metadata deadline expired",
16051
+ configuration: "the residency guard configuration is invalid"
16052
+ };
16053
+ var modelResidencyMessages = {
16054
+ blocked: (reason) => `[openteam] Lemonade model request blocked: ${reasons[reason]}.`,
16055
+ providerDisabled: (providerID) => `[openteam] Lemonade provider "${providerID}" is disabled because its configured transport or runtime mapping cannot enforce loaded-only requests.`,
16056
+ extractionRuntimeUnavailable: "[openteam] Memory extraction requires one enabled local runtime matching the configured extraction provider."
16057
+ };
16058
+
16059
+ // src/local/lemonadeResidency.ts
16060
+ class ModelResidencyError extends Error {
16061
+ code;
16062
+ statusCode = 400;
16063
+ isRetryable = false;
16064
+ constructor(code) {
16065
+ super(modelResidencyMessages.blocked(code));
16066
+ this.code = code;
16067
+ this.name = "ModelResidencyError";
16068
+ }
16069
+ }
16070
+ var modelName = z22.string().min(1).refine((name) => name === name.trim());
16071
+ var LoadedModelSchema = z22.object({
16072
+ model_name: modelName,
16073
+ loaded: z22.boolean().optional(),
16074
+ status: z22.string().optional(),
16075
+ backend_alive: z22.boolean().optional(),
16076
+ backend_health: z22.string().optional(),
16077
+ watchdog_reset: z22.boolean().optional(),
16078
+ watchdog_reset_reason: z22.string().optional(),
16079
+ residency_class: z22.string().optional(),
16080
+ slot_pool: z22.string().optional(),
16081
+ checkpoint: z22.string().optional(),
16082
+ type: z22.string().optional(),
16083
+ device: z22.string().optional(),
16084
+ backend_url: z22.string().optional(),
16085
+ pid: z22.number().int().optional(),
16086
+ launch_command: z22.unknown().optional(),
16087
+ pinned: z22.boolean().optional(),
16088
+ recipe: z22.string().optional(),
16089
+ recipe_options: z22.unknown().optional(),
16090
+ is_busy: z22.boolean().optional(),
16091
+ is_streaming: z22.boolean().optional(),
16092
+ max_context_window: z22.number().optional(),
16093
+ cost_input_per_million: z22.number().optional(),
16094
+ cost_output_per_million: z22.number().optional(),
16095
+ last_use: z22.number().optional()
16096
+ }).strict();
16097
+ var TelemetrySchema = z22.object({
16098
+ enabled: z22.boolean(),
16099
+ captures: z22.array(z22.string()).optional()
16100
+ }).strict();
16101
+ var HealthSchema = z22.object({
16102
+ status: z22.literal("ok"),
16103
+ all_models_loaded: z22.array(LoadedModelSchema),
16104
+ version: z22.string().optional(),
16105
+ model_loaded: z22.string().nullable().optional(),
16106
+ telemetry: TelemetrySchema.optional(),
16107
+ max_models: z22.record(z22.string(), z22.number().int()).optional(),
16108
+ pinned_models: z22.record(z22.string(), z22.number().int()).optional(),
16109
+ pinned_helper_models: z22.record(z22.string(), z22.number().int()).optional(),
16110
+ websocket_port: z22.number().int().optional(),
16111
+ update_check_done: z22.boolean().optional()
16112
+ }).strict();
16113
+ var AliasSchema = z22.object({
16114
+ alias: modelName,
16115
+ target: modelName,
16116
+ downloaded: z22.boolean().optional(),
16117
+ recipe: z22.string().optional()
16118
+ }).strict();
16119
+ var AliasesSchema = z22.object({
16120
+ aliases: z22.array(AliasSchema)
16121
+ }).strict();
16122
+ var MetadataRecordSchema = z22.record(z22.string(), z22.unknown());
16123
+ var inactiveStates = new Set([
16124
+ "loading",
16125
+ "downsizing",
16126
+ "downsized",
16127
+ "evicting",
16128
+ "unloaded"
16129
+ ]);
16130
+ var inactiveHealth = new Set(["stopped", "exited", "watchdog_reset"]);
16131
+ var standardPools = new Set([
16132
+ "standard/llm",
16133
+ "standard/embedding",
16134
+ "standard/reranking",
16135
+ "standard/transcription",
16136
+ "standard/image",
16137
+ "standard/tts",
16138
+ "standard/audio-generation",
16139
+ "standard/classification",
16140
+ "standard/mesh"
16141
+ ]);
16142
+ function usableState(entry) {
16143
+ if (entry.loaded === false || entry.backend_alive === false || entry.watchdog_reset === true || typeof entry.status === "string" && inactiveStates.has(entry.status) || typeof entry.backend_health === "string" && inactiveHealth.has(entry.backend_health) || entry.residency_class === "routing_helper" || typeof entry.slot_pool === "string" && entry.slot_pool.startsWith("routing_helper/")) {
16144
+ return false;
16145
+ }
16146
+ if (entry.loaded !== true || entry.backend_alive !== true || entry.watchdog_reset !== false || entry.status !== "ready" && entry.status !== "in_use" || entry.backend_health !== "ready" && entry.backend_health !== "busy" || entry.watchdog_reset_reason !== undefined && entry.watchdog_reset_reason !== "" || entry.residency_class !== undefined && entry.residency_class !== "standard" || entry.slot_pool !== undefined && (typeof entry.slot_pool !== "string" || !standardPools.has(entry.slot_pool))) {
16147
+ return;
16148
+ }
16149
+ return true;
16150
+ }
16151
+ function projectMetadataObject(payload, keys) {
16152
+ const record2 = MetadataRecordSchema.safeParse(payload);
16153
+ if (!record2.success)
16154
+ throw new ModelResidencyError("unknown");
16155
+ return Object.fromEntries(keys.filter((key) => Object.hasOwn(record2.data, key)).map((key) => [key, record2.data[key]]));
16156
+ }
16157
+ function parseResidency(healthPayload, aliasPayload) {
16158
+ const healthFields = projectMetadataObject(healthPayload, Object.keys(HealthSchema.shape));
16159
+ if (Array.isArray(healthFields.all_models_loaded)) {
16160
+ healthFields.all_models_loaded = healthFields.all_models_loaded.map((entry) => projectMetadataObject(entry, Object.keys(LoadedModelSchema.shape)));
16161
+ }
16162
+ if (healthFields.telemetry !== undefined) {
16163
+ healthFields.telemetry = projectMetadataObject(healthFields.telemetry, Object.keys(TelemetrySchema.shape));
16164
+ }
16165
+ const aliasFields = projectMetadataObject(aliasPayload, Object.keys(AliasesSchema.shape));
16166
+ if (Array.isArray(aliasFields.aliases)) {
16167
+ aliasFields.aliases = aliasFields.aliases.map((entry) => projectMetadataObject(entry, Object.keys(AliasSchema.shape)));
16168
+ }
16169
+ const health = HealthSchema.safeParse(healthFields);
16170
+ const aliases = AliasesSchema.safeParse(aliasFields);
16171
+ if (!health.success || !aliases.success)
16172
+ throw new ModelResidencyError("unknown");
16173
+ const result = new Map;
16174
+ for (const entry of health.data.all_models_loaded) {
16175
+ if (result.has(entry.model_name))
16176
+ throw new ModelResidencyError("unknown");
16177
+ result.set(entry.model_name, {
16178
+ loaded: entry.model_name.endsWith(":latest") ? undefined : usableState(entry),
16179
+ metadata: entry
16180
+ });
16181
+ }
16182
+ const seenAliases = new Set;
16183
+ for (const entry of aliases.data.aliases) {
16184
+ if (seenAliases.has(entry.alias))
16185
+ throw new ModelResidencyError("unknown");
16186
+ seenAliases.add(entry.alias);
16187
+ result.set(entry.alias, {
16188
+ loaded: undefined,
16189
+ metadata: result.get(entry.alias)?.metadata ?? {}
16190
+ });
16191
+ }
16192
+ return result;
16193
+ }
16194
+ function lemonadeBaseURL(value) {
16195
+ let url;
16196
+ try {
16197
+ url = new URL(value);
16198
+ } catch {
16199
+ throw new ModelResidencyError("destination");
16200
+ }
16201
+ if (url.protocol !== "http:" && url.protocol !== "https:" || url.username !== "" || url.password !== "" || url.search !== "" || url.hash !== "" || !/^\/(?:api\/)?v[01]\/?$/.test(url.pathname)) {
16202
+ throw new ModelResidencyError("destination");
16203
+ }
16204
+ return `${url.origin}${url.pathname.replace(/\/$/, "")}`;
16205
+ }
16206
+ async function withResidencyDeadline(operation, options) {
16207
+ const timeoutMs = options.timeoutMs ?? 5000;
16208
+ if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) {
16209
+ throw new ModelResidencyError("configuration");
16210
+ }
16211
+ const controller = new AbortController;
16212
+ const cancel = () => controller.abort(new ModelResidencyError("cancelled"));
16213
+ if (options.signal?.aborted)
16214
+ throw new ModelResidencyError("cancelled");
16215
+ options.signal?.addEventListener("abort", cancel, { once: true });
16216
+ const schedule = options.scheduleTimeout ?? ((callback, ms2) => {
16217
+ const timer = setTimeout(callback, ms2);
16218
+ return () => clearTimeout(timer);
16219
+ });
16220
+ let onAbort = () => {};
16221
+ const aborted = new Promise((_, reject) => {
16222
+ onAbort = () => reject(controller.signal.reason);
16223
+ controller.signal.addEventListener("abort", onAbort, { once: true });
16224
+ });
16225
+ const clearTimer = schedule(() => controller.abort(new ModelResidencyError("timeout")), timeoutMs);
16226
+ try {
16227
+ controller.signal.throwIfAborted();
16228
+ return await Promise.race([operation(controller.signal), aborted]);
16229
+ } finally {
16230
+ clearTimer();
16231
+ options.signal?.removeEventListener("abort", cancel);
16232
+ controller.signal.removeEventListener("abort", onAbort);
16233
+ controller.abort();
16234
+ }
16235
+ }
16236
+ async function metadata(url, fetch2, signal, headers) {
16237
+ signal.throwIfAborted();
16238
+ let payload;
16239
+ try {
16240
+ const response = await fetch2(url, {
16241
+ method: "GET",
16242
+ headers,
16243
+ signal,
16244
+ redirect: "error",
16245
+ cache: "no-store"
16246
+ });
16247
+ signal.throwIfAborted();
16248
+ if (!response.ok || response.redirected || response.url !== "" && response.url !== url) {
16249
+ throw new ModelResidencyError("unknown");
16250
+ }
16251
+ payload = await response.json();
16252
+ } catch {
16253
+ signal.throwIfAborted();
16254
+ throw new ModelResidencyError("unknown");
16255
+ }
16256
+ signal.throwIfAborted();
16257
+ return payload;
16258
+ }
16259
+ async function readLemonadeResidency(baseURL, fetch2, options = {}) {
16260
+ const base = lemonadeBaseURL(baseURL);
16261
+ const headers = new Headers(options.headers);
16262
+ headers.delete("content-type");
16263
+ headers.delete("content-length");
16264
+ headers.set("accept", "application/json");
16265
+ headers.set("cache-control", "no-cache, no-store");
16266
+ headers.delete("if-none-match");
16267
+ headers.delete("if-modified-since");
16268
+ return withResidencyDeadline(async (signal) => {
16269
+ const aliases = await metadata(`${new URL(base).origin}/internal/aliases`, fetch2, signal, headers);
16270
+ const health = await metadata(`${base}/health`, fetch2, signal, headers);
16271
+ return parseResidency(health, aliases);
16272
+ }, options);
16273
+ }
16274
+
15906
16275
  // src/local/lemonade.ts
15907
16276
  var LEMONADE_DEFAULT_BASE_URL = "http://localhost:13305/api/v1";
15908
16277
  var NON_TEXT_LABELS = new Set([
@@ -15989,36 +16358,19 @@ function isNonTextModel(entry) {
15989
16358
  return entry.labels.some((label) => typeof label === "string" && NON_TEXT_LABELS.has(label));
15990
16359
  }
15991
16360
  async function readLoadedModels(baseURL, fetch2) {
15992
- let payload;
15993
16361
  try {
15994
- const response = await fetch2(`${baseURL}/health`, {
15995
- method: "GET",
15996
- headers: { accept: "application/json" }
15997
- });
15998
- if (!response.ok) {
15999
- return;
16000
- }
16001
- payload = await response.json();
16362
+ const residency = await readLemonadeResidency(baseURL, fetch2);
16363
+ return new Map([...residency].map(([name, entry]) => [
16364
+ name,
16365
+ {
16366
+ loaded: entry.loaded,
16367
+ slots: entry.loaded === true ? readSlotCount(entry.metadata) : undefined,
16368
+ totalContextWindow: entry.loaded === true ? readLoadedContextWindow(entry.metadata) : undefined
16369
+ }
16370
+ ]));
16002
16371
  } catch {
16003
16372
  return;
16004
16373
  }
16005
- if (!isObject2(payload) || !Array.isArray(payload.all_models_loaded)) {
16006
- return;
16007
- }
16008
- const loaded = new Map;
16009
- for (const entry of payload.all_models_loaded) {
16010
- if (!isObject2(entry) || typeof entry.model_name !== "string") {
16011
- continue;
16012
- }
16013
- if (entry.loaded === false) {
16014
- continue;
16015
- }
16016
- loaded.set(entry.model_name, {
16017
- slots: readSlotCount(entry),
16018
- totalContextWindow: readLoadedContextWindow(entry)
16019
- });
16020
- }
16021
- return loaded;
16022
16374
  }
16023
16375
  function readSlotCount(entry) {
16024
16376
  return slotsFromLlamacppArgs(entry) ?? slotsFromLaunchCommand(entry);
@@ -16165,6 +16517,9 @@ function enrichWithLoadState(models, loaded) {
16165
16517
  if (hit === undefined) {
16166
16518
  return { ...base, loaded: false };
16167
16519
  }
16520
+ if (hit.loaded !== true) {
16521
+ return hit.loaded === false ? { ...base, loaded: false } : base;
16522
+ }
16168
16523
  const contextWindow = derivedConservativeContextWindow(model.contextWindow, hit);
16169
16524
  return {
16170
16525
  ...base,
@@ -16181,6 +16536,11 @@ function isObject2(value) {
16181
16536
  return typeof value === "object" && value !== null && !Array.isArray(value);
16182
16537
  }
16183
16538
 
16539
+ // src/local/modelResidency.ts
16540
+ function hasRequiredModelResidency(kind, model) {
16541
+ return kind !== "lemonade" || model?.loaded === true;
16542
+ }
16543
+
16184
16544
  // src/local/lmstudio.ts
16185
16545
  var LMSTUDIO_DEFAULT_BASE_URL = "http://localhost:1234/v1";
16186
16546
  function createLMStudioAdapter() {
@@ -16398,50 +16758,36 @@ function createDetect(exec) {
16398
16758
  foundryDiscovery: foundryDiscoveryFromExec(exec)
16399
16759
  });
16400
16760
  const snapshots = await registry.probe(DETECTION_RUNTIMES);
16401
- return snapshots.map((snapshot) => {
16402
- const detected = {
16403
- id: snapshot.id,
16404
- baseURL: snapshot.baseURL,
16405
- reachable: snapshot.reachable,
16406
- models: snapshot.models.map((model) => model.modelID)
16407
- };
16408
- if (snapshot.models.some((model) => model.loaded !== undefined)) {
16409
- detected.modelInfo = snapshot.models;
16410
- }
16411
- if (snapshot.error !== undefined) {
16412
- detected.error = snapshot.error;
16413
- }
16414
- return detected;
16415
- });
16761
+ return DETECTION_RUNTIMES.map((runtime) => detectedRuntime(runtime, snapshots.find((snapshot) => snapshot.id === runtime.id)));
16762
+ };
16763
+ }
16764
+ function detectedRuntime(runtime, snapshot) {
16765
+ const modelInfo = snapshot?.models ?? [];
16766
+ return {
16767
+ id: runtime.id,
16768
+ baseURL: snapshot?.baseURL ?? runtime.baseURL ?? "",
16769
+ reachable: snapshot?.reachable ?? false,
16770
+ models: modelInfo.filter((model) => hasRequiredModelResidency(localRuntimeKind(runtime), model)).map((model) => model.modelID),
16771
+ modelInfo,
16772
+ ...snapshot?.error === undefined ? {} : { error: snapshot.error }
16416
16773
  };
16417
16774
  }
16418
16775
  function createProbeRuntime(exec) {
16419
- return async (id, baseURL) => {
16776
+ return async (kind, baseURL) => {
16420
16777
  const registry = new RuntimeRegistry({
16421
16778
  fetch: globalThis.fetch,
16422
16779
  clock: () => new Date().toISOString(),
16423
16780
  foundryDiscovery: foundryDiscoveryFromExec(exec)
16424
16781
  });
16425
16782
  const runtime = {
16426
- id,
16783
+ id: kind,
16784
+ kind,
16427
16785
  enabled: true,
16428
16786
  baseURL,
16429
- defaultModel: { providerID: id, modelID: "probe" }
16787
+ defaultModel: { providerID: kind, modelID: "probe" }
16430
16788
  };
16431
16789
  const [snapshot] = await registry.probe([runtime]);
16432
- const detected = {
16433
- id,
16434
- baseURL: snapshot?.baseURL ?? baseURL,
16435
- reachable: snapshot?.reachable ?? false,
16436
- models: snapshot?.models.map((model) => model.modelID) ?? []
16437
- };
16438
- if (snapshot?.models.some((model) => model.loaded !== undefined) === true) {
16439
- detected.modelInfo = snapshot.models;
16440
- }
16441
- if (snapshot?.error !== undefined) {
16442
- detected.error = snapshot.error;
16443
- }
16444
- return detected;
16790
+ return detectedRuntime(runtime, snapshot);
16445
16791
  };
16446
16792
  }
16447
16793
 
@@ -16554,11 +16900,11 @@ function planWorktreeReconciliation(input) {
16554
16900
  }
16555
16901
 
16556
16902
  // src/telemetry/otelConfig.ts
16557
- import { z as z22 } from "zod";
16558
- var OtelBackendConfigSchema = z22.object({
16559
- backend: z22.literal("opentelemetry"),
16560
- connectionEnv: z22.string().min(1),
16561
- serviceName: z22.string().min(1).optional()
16903
+ import { z as z23 } from "zod";
16904
+ var OtelBackendConfigSchema = z23.object({
16905
+ backend: z23.literal("opentelemetry"),
16906
+ connectionEnv: z23.string().min(1),
16907
+ serviceName: z23.string().min(1).optional()
16562
16908
  }).strict();
16563
16909
  function parseConnectionString(raw) {
16564
16910
  const pairs = new Map;
@@ -17700,7 +18046,7 @@ import { posix as pathPosix, win32 as pathWin32 } from "node:path";
17700
18046
  // package.json
17701
18047
  var package_default = {
17702
18048
  name: "@jmanuelcorral/openteam",
17703
- version: "0.23.0",
18049
+ version: "0.24.1",
17704
18050
  packageManager: "bun@1.3.14",
17705
18051
  description: "Cost-aware, local-first routing plugin for opencode with cheapest-capable frontier fallback and multi-agent orchestration.",
17706
18052
  license: "MIT",
@@ -17844,31 +18190,31 @@ function compareSemver(a, b) {
17844
18190
  }
17845
18191
 
17846
18192
  // src/graph/soakLedger.ts
17847
- import { z as z23 } from "zod";
17848
- var Sha256Schema2 = z23.string().regex(/^[0-9a-f]{64}$/);
17849
- var SoakPlatformSchema = z23.enum(["linux", "win32", "darwin"]);
17850
- var PrivacyPairSchema = z23.object({
17851
- surfacesScanned: z23.number().int().nonnegative(),
17852
- rawFindings: z23.number().int().nonnegative()
18193
+ import { z as z24 } from "zod";
18194
+ var Sha256Schema2 = z24.string().regex(/^[0-9a-f]{64}$/);
18195
+ var SoakPlatformSchema = z24.enum(["linux", "win32", "darwin"]);
18196
+ var PrivacyPairSchema = z24.object({
18197
+ surfacesScanned: z24.number().int().nonnegative(),
18198
+ rawFindings: z24.number().int().nonnegative()
17853
18199
  }).strict();
17854
- var DuplicateEffectPairSchema = z23.object({
17855
- effectsExamined: z23.number().int().nonnegative(),
17856
- duplicatesFound: z23.number().int().nonnegative()
18200
+ var DuplicateEffectPairSchema = z24.object({
18201
+ effectsExamined: z24.number().int().nonnegative(),
18202
+ duplicatesFound: z24.number().int().nonnegative()
17857
18203
  }).strict();
17858
- var ModelVerificationPairSchema = z23.object({
17859
- nodesChecked: z23.number().int().nonnegative(),
17860
- unverified: z23.number().int().nonnegative(),
17861
- mismatched: z23.number().int().nonnegative()
18204
+ var ModelVerificationPairSchema = z24.object({
18205
+ nodesChecked: z24.number().int().nonnegative(),
18206
+ unverified: z24.number().int().nonnegative(),
18207
+ mismatched: z24.number().int().nonnegative()
17862
18208
  }).strict();
17863
- var SoakObservationSchema = z23.object({
17864
- version: z23.literal(1),
17865
- seq: z23.number().int().nonnegative(),
17866
- timestamp: z23.string().datetime(),
18209
+ var SoakObservationSchema = z24.object({
18210
+ version: z24.literal(1),
18211
+ seq: z24.number().int().nonnegative(),
18212
+ timestamp: z24.string().datetime(),
17867
18213
  platform: SoakPlatformSchema,
17868
- opencodeVersion: z23.string().min(1),
17869
- provenance: z23.enum(["genuine-usage", "ci-synthetic"]),
18214
+ opencodeVersion: z24.string().min(1),
18215
+ provenance: z24.enum(["genuine-usage", "ci-synthetic"]),
17870
18216
  traceDigest: Sha256Schema2,
17871
- criticalDivergences: z23.number().int().nonnegative().nullable(),
18217
+ criticalDivergences: z24.number().int().nonnegative().nullable(),
17872
18218
  privacy: PrivacyPairSchema.nullable(),
17873
18219
  duplicateEffects: DuplicateEffectPairSchema.nullable(),
17874
18220
  modelVerification: ModelVerificationPairSchema.nullable(),
@@ -18054,57 +18400,6 @@ var GENERATED_OPENTEAM_STATE_GROUPS = [
18054
18400
  { kind: "file", paths: [SOAK_RECORDER_ID_PATH] }
18055
18401
  ];
18056
18402
  var OPENCODE_DIR = ".opencode";
18057
- var AGENT_DIR = `${OPENCODE_DIR}/agent`;
18058
- var ROLE_AGENT_DESCRIPTIONS = {
18059
- architect: "openteam architect subagent: high-level design, cross-cutting refactors and hard technical decisions.",
18060
- integration: "openteam integration subagent: wires components together, owns end-to-end flows and cross-module glue.",
18061
- "local-runtime": "openteam local-runtime subagent: local model operation, provider binding and runtime health.",
18062
- "routing-cost": "openteam routing-cost subagent: cost-aware routing analysis and token/budget optimisation.",
18063
- scribe: "openteam scribe subagent: the team's silent memory. Records decisions and learnings in .opencode/openteam/decisions.md without changing code. When a conversation yields durable, reusable knowledge — such as facts, entities, and the typed relations between them (e.g. a decision that affects a component) — also call the `openteam-memory` tool with those messages to record it in the team's semantic graph; it never stores the raw conversation, and a reply that memory is disabled or that nothing was recorded is expected, so do not retry.",
18064
- ralph: "openteam ralph subagent: automation and triage driver. Monitors pending work, prioritises it and coordinates execution, escalating blockers to the human.",
18065
- guardian: "openteam guardian subagent: an assignable security reviewer for threat analysis, exploitability checks and security-focused review.",
18066
- tester: "openteam tester subagent: writes and runs tests, reproduces bugs and validates fixes.",
18067
- reviewer: "openteam reviewer subagent: reviews diffs for correctness, security and quality."
18068
- };
18069
- function buildRoleAgent(agentName, description) {
18070
- const frontmatter = [
18071
- "---",
18072
- `description: ${JSON.stringify(description)}`,
18073
- "mode: subagent",
18074
- "temperature: 0.2",
18075
- "---"
18076
- ].join(`
18077
- `);
18078
- const body = [
18079
- `You are the **${agentName}** subagent of openteam.`,
18080
- "",
18081
- description,
18082
- "",
18083
- "Your execution domain, selected model and ordered fallbacks come from the matching policy in `.opencode/openteam.json`; leave `model` unset here so the runtime policy remains authoritative.",
18084
- "",
18085
- "Stay focused on your role. Keep changes surgical and report what you did."
18086
- ].join(`
18087
- `);
18088
- return `${frontmatter}
18089
-
18090
- ${body}
18091
- `;
18092
- }
18093
- function buildRoleAgentFiles(_config) {
18094
- const seen = new Set;
18095
- for (const role of Object.values(TEAM_ROLES)) {
18096
- const agentName = role.opencodeAgent;
18097
- if (agentName === undefined || seen.has(agentName)) {
18098
- continue;
18099
- }
18100
- seen.add(agentName);
18101
- }
18102
- return [...seen].sort((a, b) => a.localeCompare(b)).map((agentName) => ({
18103
- agentName,
18104
- path: `${AGENT_DIR}/${agentName}.md`,
18105
- contents: buildRoleAgent(agentName, ROLE_AGENT_DESCRIPTIONS[agentName] ?? `openteam ${agentName} subagent.`)
18106
- }));
18107
- }
18108
18403
  function splitSetupPath(path2) {
18109
18404
  return path2.split("/").filter((segment) => segment.length > 0);
18110
18405
  }
@@ -18201,6 +18496,12 @@ var KNOWN_RUNTIMES = [
18201
18496
  dynamicPort: true
18202
18497
  }
18203
18498
  ];
18499
+ var LEMONADE_PROJECT_CONFIG_CANDIDATES = [
18500
+ ".opencode/opencode.jsonc",
18501
+ ".opencode/opencode.json",
18502
+ "opencode.jsonc",
18503
+ "opencode.json"
18504
+ ];
18204
18505
  function knownRuntime(id) {
18205
18506
  const found = KNOWN_RUNTIMES.find((runtime) => runtime.id === id);
18206
18507
  if (found === undefined) {
@@ -18217,7 +18518,44 @@ function choiceKind(choice) {
18217
18518
  function choiceProviderID(choice) {
18218
18519
  return choice.providerID ?? choice.id;
18219
18520
  }
18521
+ function selectableModelIDs(models, modelInfo, kind) {
18522
+ const byID = new Map(modelInfo?.map((model) => [model.modelID, model]));
18523
+ return models.filter((id) => hasRequiredModelResidency(kind, byID.get(id)));
18524
+ }
18525
+ function assertRuntimeResidency(choice, primary) {
18526
+ const kind = localRuntimeKind(choice);
18527
+ const references2 = [
18528
+ choice.defaultModelID,
18529
+ choice.thinkingModelID,
18530
+ choice.coderModelID,
18531
+ ...primary.providerID === choiceProviderID(choice) ? [primary.modelID] : []
18532
+ ];
18533
+ for (const id of references2) {
18534
+ if (id === undefined)
18535
+ continue;
18536
+ const info = choice.models.includes(id) ? choice.modelInfo?.find((model) => model.modelID === id) : undefined;
18537
+ if (!hasRequiredModelResidency(kind, info))
18538
+ throw new Error(executionSetupMessages.lemonadeModelNotLoaded(`${choiceProviderID(choice)}/${id}`));
18539
+ }
18540
+ }
18541
+ function assertSetupResidency(answers) {
18542
+ for (const choice of answers.runtimes) {
18543
+ if (choice.enabled) {
18544
+ assertRuntimeResidency(choice, answers.primaryModel);
18545
+ } else if (choiceProviderID(choice) === answers.primaryModel.providerID && !hasRequiredModelResidency(localRuntimeKind(choice), undefined)) {
18546
+ throw new Error(executionSetupMessages.lemonadeModelNotLoaded(`${answers.primaryModel.providerID}/${answers.primaryModel.modelID}`));
18547
+ }
18548
+ }
18549
+ }
18550
+ function lemonadeV2Policies(providers) {
18551
+ return [...new Set(providers)].sort().map((providerID) => {
18552
+ if (providerID.length === 0 || /[*?\\/\s]/.test(providerID))
18553
+ throw new Error(executionSetupMessages.lemonadeV2InvalidProvider(providerID));
18554
+ return { action: "provider.use", resource: providerID, effect: "deny" };
18555
+ });
18556
+ }
18220
18557
  function buildOpenTeamConfig(answers) {
18558
+ assertSetupResidency(answers);
18221
18559
  const runtimes = answers.runtimes.filter((choice) => choice.enabled).map((choice) => {
18222
18560
  const runtime = {
18223
18561
  id: choice.id,
@@ -18286,6 +18624,8 @@ function orchestratorAgentModel(answers) {
18286
18624
  return { ...answers.primaryModel };
18287
18625
  }
18288
18626
  function buildOpencodeConfig(answers) {
18627
+ assertSetupResidency(answers);
18628
+ const policies = lemonadeV2Policies(answers.runtimes.filter((choice) => choice.enabled && choiceKind(choice) === "lemonade").map(choiceProviderID));
18289
18629
  const provider = {};
18290
18630
  const providerOwner = new Map;
18291
18631
  for (const choice of answers.runtimes) {
@@ -18298,7 +18638,7 @@ function buildOpencodeConfig(answers) {
18298
18638
  continue;
18299
18639
  }
18300
18640
  const providerID = choiceProviderID(choice);
18301
- const modelIDs = new Set(choice.models);
18641
+ const modelIDs = new Set(selectableModelIDs(choice.models, choice.modelInfo, meta.id));
18302
18642
  modelIDs.add(choice.defaultModelID);
18303
18643
  if (choice.thinkingModelID !== undefined) {
18304
18644
  modelIDs.add(choice.thinkingModelID);
@@ -18354,6 +18694,8 @@ function buildOpencodeConfig(answers) {
18354
18694
  if (answers.yolo) {
18355
18695
  config.permission = { "*": "allow" };
18356
18696
  }
18697
+ if (policies.length > 0)
18698
+ config.experimental = { policies };
18357
18699
  return config;
18358
18700
  }
18359
18701
  function serializeOpencodeConfig(config) {
@@ -18453,10 +18795,11 @@ async function chooseFrontier(prompt, profiles) {
18453
18795
  }
18454
18796
  return parseFrontierRef(selected);
18455
18797
  }
18456
- function orderModelChoices(models, modelInfo) {
18798
+ function orderModelChoices(models, modelInfo, kind) {
18457
18799
  const byID = new Map((modelInfo ?? []).map((model) => [model.modelID, model]));
18458
18800
  const anyLoadState = (modelInfo ?? []).some((model) => model.loaded !== undefined);
18459
- const decorated = models.map((modelID, index) => {
18801
+ const offered = kind === undefined ? models : selectableModelIDs(models, modelInfo, kind);
18802
+ const decorated = offered.map((modelID, index) => {
18460
18803
  const info = byID.get(modelID);
18461
18804
  return {
18462
18805
  modelID,
@@ -18482,27 +18825,34 @@ function modelStateSuffix(loaded, slots) {
18482
18825
  }
18483
18826
  return slots === undefined ? "(loaded)" : `(loaded · ${slots} slot${slots === 1 ? "" : "s"})`;
18484
18827
  }
18485
- function preferredModelID(models, modelInfo) {
18486
- const loaded = (modelInfo ?? []).find((model) => model.loaded === true && models.includes(model.modelID));
18487
- return loaded?.modelID ?? models[0];
18828
+ function preferredModelID(models, modelInfo, kind) {
18829
+ const offered = kind === undefined ? models : selectableModelIDs(models, modelInfo, kind);
18830
+ const loaded = (modelInfo ?? []).find((model) => model.loaded === true && offered.includes(model.modelID));
18831
+ return loaded?.modelID ?? offered[0];
18488
18832
  }
18489
18833
  async function chooseRuntimeModel(prompt, models, meta, modelInfo) {
18490
- if (models.length === 0) {
18834
+ const offered = selectableModelIDs(models, modelInfo, meta.id);
18835
+ if (offered.length === 0) {
18836
+ if (meta.id === "lemonade")
18837
+ throw new Error(executionSetupMessages.lemonadeNoLoadedModels);
18491
18838
  return prompt.text({
18492
18839
  message: `Default model for ${meta.label} (no models detected)`,
18493
18840
  placeholder: meta.fallbackModelID,
18494
18841
  initial: meta.fallbackModelID
18495
18842
  });
18496
18843
  }
18497
- const first = preferredModelID(models, modelInfo);
18844
+ const first = preferredModelID(offered, modelInfo, meta.id);
18498
18845
  const opts = {
18499
18846
  message: `Default local model for ${meta.label}`,
18500
- choices: orderModelChoices(models, modelInfo)
18847
+ choices: orderModelChoices(offered, modelInfo, meta.id)
18501
18848
  };
18502
18849
  if (first !== undefined) {
18503
18850
  opts.initial = first;
18504
18851
  }
18505
- return prompt.select(opts);
18852
+ const selected = await prompt.select(opts);
18853
+ if (meta.id === "lemonade" && !offered.includes(selected))
18854
+ throw new Error(executionSetupMessages.lemonadeModelNotLoaded(selected));
18855
+ return selected;
18506
18856
  }
18507
18857
  function remoteBaseURLExample(meta) {
18508
18858
  const base = meta.defaultBaseURL ?? "http://localhost:11434/v1";
@@ -18557,6 +18907,8 @@ async function resolveRuntimeConnection(deps, meta, info) {
18557
18907
  initial: RUNTIME_LOCATION_LOCAL
18558
18908
  });
18559
18909
  if (location === RUNTIME_LOCATION_LOCAL) {
18910
+ if (meta.id === "lemonade" && info?.reachable !== true)
18911
+ throw new Error(executionSetupMessages.lemonadeProbeFailed);
18560
18912
  const baseURL2 = info?.baseURL !== undefined && info.baseURL.length > 0 ? info.baseURL : meta.defaultBaseURL;
18561
18913
  return { baseURL: baseURL2, ...connectionOf(info) };
18562
18914
  }
@@ -18566,16 +18918,26 @@ async function resolveRuntimeConnection(deps, meta, info) {
18566
18918
  });
18567
18919
  const baseURL = normalizeRemoteBaseURL(entered, meta.defaultBaseURL);
18568
18920
  let probedRuntime;
18921
+ if (meta.id === "lemonade" && deps.probeRuntime === undefined)
18922
+ throw new Error(executionSetupMessages.lemonadeProbeRequired);
18569
18923
  if (deps.probeRuntime !== undefined) {
18570
18924
  try {
18571
18925
  probedRuntime = await deps.probeRuntime(meta.id, baseURL);
18572
- } catch {}
18926
+ } catch (cause) {
18927
+ if (meta.id === "lemonade")
18928
+ throw new Error(executionSetupMessages.lemonadeProbeFailed, { cause });
18929
+ }
18930
+ if (meta.id === "lemonade" && probedRuntime?.reachable !== true)
18931
+ throw new Error(executionSetupMessages.lemonadeProbeFailed);
18573
18932
  }
18574
18933
  return { baseURL, ...connectionOf(probedRuntime) };
18575
18934
  }
18576
18935
  async function chooseClassModel(prompt, models, meta, defaultModelID, modelClass, modelInfo) {
18577
18936
  const label = modelClass === "thinking" ? `Thinking model for ${meta.label} (design, analysis, review reasoning)` : `Coder model for ${meta.label} (writing and editing code)`;
18578
- if (models.length === 0) {
18937
+ const offered = selectableModelIDs(models, modelInfo, meta.id);
18938
+ if (offered.length === 0) {
18939
+ if (meta.id === "lemonade")
18940
+ throw new Error(executionSetupMessages.lemonadeNoLoadedModels);
18579
18941
  const raw = await prompt.text({
18580
18942
  message: `${label} — enter to reuse the default`,
18581
18943
  placeholder: defaultModelID,
@@ -18583,14 +18945,17 @@ async function chooseClassModel(prompt, models, meta, defaultModelID, modelClass
18583
18945
  });
18584
18946
  return raw.trim().length > 0 ? raw.trim() : defaultModelID;
18585
18947
  }
18586
- return prompt.select({
18948
+ const selected = await prompt.select({
18587
18949
  message: label,
18588
- choices: orderModelChoices(models, modelInfo).map((choice) => ({
18950
+ choices: orderModelChoices(offered, modelInfo, meta.id).map((choice) => ({
18589
18951
  value: choice.value,
18590
18952
  label: choice.value === defaultModelID ? `${choice.label} — default` : choice.label
18591
18953
  })),
18592
18954
  initial: defaultModelID
18593
18955
  });
18956
+ if (meta.id === "lemonade" && !offered.includes(selected))
18957
+ throw new Error(executionSetupMessages.lemonadeModelNotLoaded(selected));
18958
+ return selected;
18594
18959
  }
18595
18960
  async function resolveEnabledRuntime(deps, meta, info) {
18596
18961
  const connection = await resolveRuntimeConnection(deps, meta, info);
@@ -18603,7 +18968,8 @@ async function resolveEnabledRuntime(deps, meta, info) {
18603
18968
  defaultModelID,
18604
18969
  thinkingModelID,
18605
18970
  coderModelID,
18606
- models: connection.models
18971
+ models: selectableModelIDs(connection.models, connection.modelInfo, meta.id),
18972
+ ...connection.modelInfo === undefined ? {} : { modelInfo: connection.modelInfo }
18607
18973
  };
18608
18974
  if (connection.baseURL !== undefined) {
18609
18975
  choice.baseURL = connection.baseURL;
@@ -19103,17 +19469,52 @@ async function readExistingOpencodeConfigFiles(paths, deps) {
19103
19469
  function opencodeFileAt(files, path2) {
19104
19470
  return files.find((file) => file.path === path2);
19105
19471
  }
19106
- function mergeExistingOpencodeConfigFiles(files) {
19472
+ function mergeExistingOpencodeConfigFiles(files, paths, planned = new Map) {
19107
19473
  let merged;
19108
- for (const path2 of [...OPENCODE_CONFIG_CANDIDATES].reverse()) {
19109
- const file = opencodeFileAt(files, path2);
19110
- if (file === undefined) {
19474
+ for (const path2 of [...paths].reverse()) {
19475
+ const raw = planned.get(path2) ?? opencodeFileAt(files, path2)?.raw;
19476
+ if (raw === undefined) {
19111
19477
  continue;
19112
19478
  }
19113
- merged = merged === undefined ? file.raw : mergeOpencodeConfigs(merged, file.raw);
19479
+ merged = merged === undefined ? raw : mergeOpencodeConfigs(merged, raw);
19114
19480
  }
19115
19481
  return merged;
19116
19482
  }
19483
+ async function effectiveSetupConfig(portable, files, deps) {
19484
+ const optionsByLoader = new Map;
19485
+ for (const file of files) {
19486
+ if (!Array.isArray(file.raw.plugin))
19487
+ continue;
19488
+ for (const entry of [...file.raw.plugin].reverse()) {
19489
+ const spec = extractPluginSpec(entry);
19490
+ if (spec === undefined)
19491
+ continue;
19492
+ let identity;
19493
+ if (isOpenteamNpmSpec(spec)) {
19494
+ identity = OPENTEAM_PACKAGE_NAME;
19495
+ } else if (isLocalFileSpec(spec) && await classifyLocalPluginSpecForSetup(spec, file.path, deps.classifyLocalPluginSpec) === "ours") {
19496
+ const resolved = resolvePluginSpecPath(spec, file.path);
19497
+ if (resolved === undefined)
19498
+ throw new Error(executionSetupMessages.lemonadeEffectiveConfigAmbiguous);
19499
+ identity = resolved;
19500
+ } else {
19501
+ continue;
19502
+ }
19503
+ if (!optionsByLoader.has(identity)) {
19504
+ optionsByLoader.set(identity, Array.isArray(entry) ? entry[1] : undefined);
19505
+ }
19506
+ }
19507
+ }
19508
+ const overlayText = await deps.readFile(DEFAULT_LOCAL_OVERLAY_PATH);
19509
+ const overlay = overlayText === undefined ? undefined : parseConfigFileJson(DEFAULT_LOCAL_OVERLAY_PATH, overlayText);
19510
+ const resolutions = [
19511
+ ...optionsByLoader.size > 0 ? optionsByLoader.values() : [undefined]
19512
+ ].map((options) => validateConfigInput(executionSetupMessages.lemonadeEffectiveConfigLabel, mergeConfigInputs(mergeConfigInputs(portable, configOptionsFromPluginOptions(options)), overlay)));
19513
+ const effective = resolutions[0];
19514
+ if (effective === undefined || resolutions.some((config) => JSON.stringify(config) !== JSON.stringify(effective)))
19515
+ throw new Error(executionSetupMessages.lemonadeEffectiveConfigAmbiguous);
19516
+ return effective;
19517
+ }
19117
19518
  function relevantLocalProviderIDs(generated, existingOpenTeamConfig) {
19118
19519
  const ids = new Set(Object.keys(generated.provider ?? {}));
19119
19520
  for (const runtime of existingOpenTeamConfig?.local.runtimes ?? []) {
@@ -19290,15 +19691,42 @@ function highestExistingPinVersion(files) {
19290
19691
  }
19291
19692
  return highest;
19292
19693
  }
19694
+ function reconcileLemonadeV2Policies(raw, denials) {
19695
+ if (denials.length === 0)
19696
+ return raw;
19697
+ const experimental = raw.experimental;
19698
+ if (experimental !== undefined && !isRecord6(experimental))
19699
+ throw new Error(executionSetupMessages.lemonadeV2InvalidPolicies);
19700
+ const policies = experimental?.policies;
19701
+ if (policies !== undefined && !Array.isArray(policies))
19702
+ throw new Error(executionSetupMessages.lemonadeV2InvalidPolicies);
19703
+ const existing = policies ?? [];
19704
+ for (const policy of existing) {
19705
+ if (!isRecord6(policy) || policy.action !== "provider.use" || typeof policy.resource !== "string" || policy.effect !== "allow" && policy.effect !== "deny")
19706
+ throw new Error(executionSetupMessages.lemonadeV2InvalidPolicies);
19707
+ }
19708
+ const terminal = denials.every((denial, index) => {
19709
+ const policy = existing[existing.length - denials.length + index];
19710
+ return isRecord6(policy) && Object.keys(policy).length === 3 && policy.action === denial.action && policy.resource === denial.resource && policy.effect === denial.effect;
19711
+ });
19712
+ if (terminal)
19713
+ return raw;
19714
+ return {
19715
+ ...raw,
19716
+ experimental: { ...experimental, policies: [...existing, ...denials] }
19717
+ };
19718
+ }
19293
19719
  function mergeOpencodeConfigForRerun(generated, targetRaw, options) {
19294
19720
  const effectiveRaw = options.effectiveRaw ?? targetRaw;
19295
19721
  const includePlugin = options.includePlugin ?? true;
19722
+ const managesV2Policies = (generated.experimental?.policies.length ?? 0) > 0;
19296
19723
  const managedKeys = new Set([
19297
19724
  "$schema",
19298
19725
  "provider",
19299
19726
  "model",
19300
19727
  "small_model",
19301
19728
  "permission",
19729
+ ...managesV2Policies ? ["experimental"] : [],
19302
19730
  ...includePlugin ? ["plugin"] : []
19303
19731
  ]);
19304
19732
  const unknownFields = {};
@@ -19361,7 +19789,10 @@ function mergeOpencodeConfigForRerun(generated, targetRaw, options) {
19361
19789
  if (permission !== undefined) {
19362
19790
  result.permission = permission;
19363
19791
  }
19364
- return result;
19792
+ if (managesV2Policies && hasOwn2(targetRaw, "experimental")) {
19793
+ result.experimental = targetRaw.experimental;
19794
+ }
19795
+ return reconcileLemonadeV2Policies(result, generated.experimental?.policies ?? []);
19365
19796
  }
19366
19797
  function mergeOpenTeamConfigForRerun(generated, existing) {
19367
19798
  const existingOrch = existing.orchestrator;
@@ -19411,6 +19842,21 @@ function mergeOpenTeamConfigForRerun(generated, existing) {
19411
19842
  assertEffectiveConfigModelDomains(merged);
19412
19843
  return merged;
19413
19844
  }
19845
+ function preserveRetainedLemonadeRuntimes(generated, previous, opencode) {
19846
+ const providers = recordAt2(opencode, "provider") ?? {};
19847
+ const retained = (previous?.local.runtimes ?? []).filter((runtime) => localRuntimeKind(runtime) === "lemonade" && hasOwn2(providers, runtime.defaultModel.providerID));
19848
+ const deselected = retained.filter((runtime) => !generated.local.runtimes.some((selected) => selected.defaultModel.providerID === runtime.defaultModel.providerID)).map((runtime) => ({ ...runtime, enabled: false }));
19849
+ return {
19850
+ config: deselected.length === 0 ? generated : OpenTeamConfigSchema.parse({
19851
+ ...generated,
19852
+ local: {
19853
+ ...generated.local,
19854
+ runtimes: [...generated.local.runtimes, ...deselected]
19855
+ }
19856
+ }),
19857
+ providerIDs: retained.map((runtime) => runtime.defaultModel.providerID)
19858
+ };
19859
+ }
19414
19860
  async function existingPluginOwnerPath(files, classifyLocalPluginSpec) {
19415
19861
  let unknownLocalPlugin;
19416
19862
  for (const file of files) {
@@ -19438,6 +19884,86 @@ async function confirmJsonNormalization(prompt, path2, format) {
19438
19884
  initial: false
19439
19885
  });
19440
19886
  }
19887
+ function assertPreviousLemonadePrimary(answers, previousConfig) {
19888
+ const priorLemonadePrimary = previousConfig?.local.runtimes.some((runtime) => runtime.defaultModel.providerID === answers.primaryModel.providerID && localRuntimeKind(runtime) === "lemonade");
19889
+ if (priorLemonadePrimary && !answers.runtimes.some((choice) => choice.enabled && choiceProviderID(choice) === answers.primaryModel.providerID)) {
19890
+ throw new Error(executionSetupMessages.lemonadeModelNotLoaded(`${answers.primaryModel.providerID}/${answers.primaryModel.modelID}`));
19891
+ }
19892
+ }
19893
+ async function verifyLemonadeBeforeWrite(deps, answers, config, opencode) {
19894
+ for (const choice of answers.runtimes) {
19895
+ if (choice.enabled && localRuntimeKind(choice) === "lemonade" && !config.local.runtimes.some((runtime) => runtime.enabled && runtime.defaultModel.providerID === choiceProviderID(choice) && localRuntimeKind(runtime) === "lemonade"))
19896
+ throw new Error(executionSetupMessages.lemonadeEndpointMismatch);
19897
+ }
19898
+ for (const runtime of config.local.runtimes) {
19899
+ if (localRuntimeKind(runtime) !== "lemonade")
19900
+ continue;
19901
+ const providerID = runtime.defaultModel.providerID;
19902
+ const provider = isRecord6(opencode.provider) ? opencode.provider[providerID] : undefined;
19903
+ if (!runtime.enabled && provider === undefined)
19904
+ continue;
19905
+ const endpoint = isRecord6(provider) ? (isRecord6(provider.options) ? provider.options.baseURL : undefined) ?? provider.api : undefined;
19906
+ if (typeof endpoint !== "string" || lemonadeBaseURL(endpoint) !== lemonadeBaseURL(runtime.baseURL ?? LEMONADE_DEFAULT_BASE_URL) || config.local.runtimes.filter((candidate) => candidate.defaultModel.providerID === providerID).length !== 1)
19907
+ throw new Error(executionSetupMessages.lemonadeEndpointMismatch);
19908
+ if (!runtime.enabled)
19909
+ continue;
19910
+ if (deps.probeRuntime === undefined)
19911
+ throw new Error(executionSetupMessages.lemonadeProbeRequired);
19912
+ const references2 = new Set([
19913
+ runtime.defaultModel.modelID,
19914
+ runtime.thinkingModel,
19915
+ runtime.coderModel
19916
+ ]);
19917
+ for (const choice of answers.runtimes) {
19918
+ if (choice.enabled && choiceProviderID(choice) === providerID) {
19919
+ for (const id of [
19920
+ ...choice.models,
19921
+ choice.defaultModelID,
19922
+ choice.thinkingModelID,
19923
+ choice.coderModelID
19924
+ ])
19925
+ references2.add(id);
19926
+ }
19927
+ }
19928
+ for (const ref of [
19929
+ answers.primaryModel,
19930
+ config.orchestrator?.primary?.model,
19931
+ config.router.localDefault
19932
+ ]) {
19933
+ if (ref !== null && typeof ref === "object" && ref.providerID === providerID)
19934
+ references2.add(ref.modelID);
19935
+ }
19936
+ for (const key of ["model", "small_model"]) {
19937
+ const ref = opencode[key];
19938
+ if (typeof ref === "string" && ref.startsWith(`${providerID}/`))
19939
+ references2.add(ref.slice(providerID.length + 1));
19940
+ }
19941
+ const models = isRecord6(provider) ? recordAt2(provider, "models") : undefined;
19942
+ const wireIDs = new Set;
19943
+ for (const alias of references2) {
19944
+ if (alias === undefined)
19945
+ continue;
19946
+ const declaration = recordAt2(models, alias);
19947
+ const wireID = declaration !== undefined && hasOwn2(declaration, "id") ? declaration.id : alias;
19948
+ if (declaration === undefined || typeof wireID !== "string" || wireID.length === 0 || wireID !== wireID.trim())
19949
+ throw new Error(executionSetupMessages.lemonadeModelIdentityUnknown(`${providerID}/${alias}`));
19950
+ wireIDs.add(wireID);
19951
+ }
19952
+ let fresh;
19953
+ try {
19954
+ fresh = await deps.probeRuntime(localRuntimeKind(runtime), endpoint);
19955
+ } catch (cause) {
19956
+ throw new Error(executionSetupMessages.lemonadeProbeFailed, { cause });
19957
+ }
19958
+ if (!fresh.reachable || fresh.id !== localRuntimeKind(runtime) || fresh.baseURL.replace(/\/+$/, "") !== endpoint.replace(/\/+$/, ""))
19959
+ throw new Error(executionSetupMessages.lemonadeProbeFailed);
19960
+ for (const wireID of wireIDs) {
19961
+ const info = fresh.models.includes(wireID) ? fresh.modelInfo?.find((model) => model.modelID === wireID) : undefined;
19962
+ if (!hasRequiredModelResidency("lemonade", info))
19963
+ throw new Error(executionSetupMessages.lemonadeModelNotLoaded(`${providerID}/${wireID}`));
19964
+ }
19965
+ }
19966
+ }
19441
19967
  async function runSetup(deps) {
19442
19968
  const { prompt } = deps;
19443
19969
  try {
@@ -19449,7 +19975,7 @@ async function runSetup(deps) {
19449
19975
  return {
19450
19976
  value: meta.id,
19451
19977
  label: meta.label,
19452
- hint: info?.reachable ? `detected · ${info.models.length} model(s)` : "not detected (you can point it to another machine)",
19978
+ hint: info?.reachable ? meta.id === "lemonade" ? executionSetupMessages.lemonadeDetectedHint(selectableModelIDs(info.models, info.modelInfo, meta.id).length, info.modelInfo?.length ?? info.models.length) : `detected · ${info.models.length} model(s)` : "not detected (you can point it to another machine)",
19453
19979
  selected: info?.reachable ?? false
19454
19980
  };
19455
19981
  });
@@ -19566,17 +20092,18 @@ async function runSetup(deps) {
19566
20092
  };
19567
20093
  const openTeamConfig = buildOpenTeamConfig(answers);
19568
20094
  const opencodeConfig = buildOpencodeConfig(answers);
20095
+ const opencodeConfigPaths = LEMONADE_PROJECT_CONFIG_CANDIDATES;
19569
20096
  const orchestratorAgent = buildOrchestratorAgent(orchestratorAgentModel(answers), { yolo });
19570
20097
  const slashCommands = buildOpenteamCommands();
19571
20098
  const openTeamPath = DEFAULT_CONFIG_PATH;
19572
- const existingOpencodePath = await deps.resolveOpencodeWriteTarget(OPENCODE_CONFIG_CANDIDATES);
20099
+ const existingOpencodePath = await deps.resolveOpencodeWriteTarget(opencodeConfigPaths);
19573
20100
  const opencodeDataPath = existingOpencodePath ?? OPENCODE_CONFIG_CANDIDATES[0];
19574
20101
  const agentPath = ORCHESTRATOR_AGENT_PATH;
19575
20102
  let existingOpenTeamConfig;
19576
20103
  let existingOpencodeFiles;
19577
20104
  try {
19578
20105
  existingOpenTeamConfig = await readExistingOpenTeamConfig(openTeamPath, deps);
19579
- existingOpencodeFiles = await readExistingOpencodeConfigFiles(OPENCODE_CONFIG_CANDIDATES, deps);
20106
+ existingOpencodeFiles = await readExistingOpencodeConfigFiles(opencodeConfigPaths, deps);
19580
20107
  } catch (error) {
19581
20108
  if (error instanceof ConfigParseError) {
19582
20109
  return {
@@ -19586,7 +20113,29 @@ async function runSetup(deps) {
19586
20113
  }
19587
20114
  throw error;
19588
20115
  }
19589
- const effectiveExistingOpencode = mergeExistingOpencodeConfigFiles(existingOpencodeFiles);
20116
+ assertPreviousLemonadePrimary(answers, existingOpenTeamConfig);
20117
+ const effectiveExistingOpencode = mergeExistingOpencodeConfigFiles(existingOpencodeFiles, opencodeConfigPaths);
20118
+ const {
20119
+ config: finalOpenTeamConfig,
20120
+ providerIDs: retainedLemonadeProviderIDs
20121
+ } = preserveRetainedLemonadeRuntimes(existingOpenTeamConfig !== undefined ? mergeOpenTeamConfigForRerun(openTeamConfig, existingOpenTeamConfig) : openTeamConfig, existingOpenTeamConfig, effectiveExistingOpencode);
20122
+ assertEffectiveConfigModelDomains(finalOpenTeamConfig);
20123
+ const effectiveOpenTeamConfig = await effectiveSetupConfig(finalOpenTeamConfig, existingOpencodeFiles, deps);
20124
+ assertEffectiveConfigModelDomains(effectiveOpenTeamConfig);
20125
+ for (const providerID of retainedLemonadeProviderIDs) {
20126
+ const matches = effectiveOpenTeamConfig.local.runtimes.filter((runtime) => runtime.defaultModel.providerID === providerID);
20127
+ if (matches.length !== 1 || matches[0] === undefined || localRuntimeKind(matches[0]) !== "lemonade")
20128
+ throw new Error(executionSetupMessages.lemonadeRetainedProviderUnprotected(providerID));
20129
+ }
20130
+ const v2Denials = lemonadeV2Policies([
20131
+ ...opencodeConfig.experimental?.policies.map((policy) => policy.resource) ?? [],
20132
+ ...effectiveOpenTeamConfig.local.runtimes.filter((runtime) => localRuntimeKind(runtime) === "lemonade").map((runtime) => runtime.defaultModel.providerID)
20133
+ ]);
20134
+ if (v2Denials.length > 0)
20135
+ opencodeConfig.experimental = { policies: v2Denials };
20136
+ const v2Notice = v2Denials.length > 0 ? executionSetupMessages.lemonadeV2Restriction(v2Denials.map((policy) => policy.resource)) : undefined;
20137
+ if (v2Notice !== undefined)
20138
+ prompt.note(v2Notice, executionSetupMessages.lemonadeV2Title);
19590
20139
  const relevantProviderIDs = relevantLocalProviderIDs(opencodeConfig, existingOpenTeamConfig);
19591
20140
  for (const file of existingOpencodeFiles) {
19592
20141
  assertRelevantLimitNumbers(file, relevantProviderIDs);
@@ -19615,8 +20164,6 @@ async function runSetup(deps) {
19615
20164
  const pluginOwnerFile = opencodeFileAt(existingOpencodeFiles, pluginOwnerPath);
19616
20165
  const pluginPlan = await mergePluginEntriesForRerun(Array.isArray(pluginOwnerFile?.raw.plugin) ? pluginOwnerFile.raw.plugin : [], pluginOwnerPath, preservePlugin, deps.classifyLocalPluginSpec);
19617
20166
  const includePluginInDataFile = pluginOwnerPath === opencodeDataPath;
19618
- const finalOpenTeamConfig = existingOpenTeamConfig !== undefined ? mergeOpenTeamConfigForRerun(openTeamConfig, existingOpenTeamConfig) : openTeamConfig;
19619
- assertEffectiveConfigModelDomains(finalOpenTeamConfig);
19620
20167
  const dataTargetRaw = opencodeFileAt(existingOpencodeFiles, opencodeDataPath)?.raw ?? {};
19621
20168
  const mergedOpencodeRaw = mergeOpencodeConfigForRerun(opencodeConfig, dataTargetRaw, {
19622
20169
  ...effectiveExistingOpencode !== undefined ? { effectiveRaw: effectiveExistingOpencode } : {},
@@ -19631,7 +20178,7 @@ async function runSetup(deps) {
19631
20178
  providerIDs: relevantProviderIDs
19632
20179
  });
19633
20180
  const opencodeWritePlan = new Map([
19634
- [opencodeDataPath, serializeOpencodeConfig(repairedDataTarget.raw)]
20181
+ [opencodeDataPath, repairedDataTarget.raw]
19635
20182
  ]);
19636
20183
  for (const file of existingOpencodeFiles) {
19637
20184
  if (file.path === opencodeDataPath) {
@@ -19643,17 +20190,16 @@ async function runSetup(deps) {
19643
20190
  generated: opencodeConfig,
19644
20191
  providerIDs: relevantProviderIDs
19645
20192
  });
19646
- let plannedRaw = repaired.raw;
19647
- let changed = repaired.changed;
20193
+ let plannedRaw = reconcileLemonadeV2Policies(repaired.raw, v2Denials);
20194
+ let changed = repaired.changed || plannedRaw !== repaired.raw;
19648
20195
  if (file.path === pluginOwnerPath && !includePluginInDataFile && pluginPlan.changed) {
19649
20196
  plannedRaw = { ...plannedRaw, plugin: [...pluginPlan.plugins] };
19650
20197
  changed = true;
19651
20198
  }
19652
20199
  if (changed) {
19653
- opencodeWritePlan.set(file.path, serializeOpencodeConfig(plannedRaw));
20200
+ opencodeWritePlan.set(file.path, plannedRaw);
19654
20201
  }
19655
20202
  }
19656
- const roleAgentFiles = buildRoleAgentFiles(finalOpenTeamConfig);
19657
20203
  const opencodeGitignorePath = OPENCODE_GITIGNORE_PATH;
19658
20204
  const existingGitignore = await deps.fileExists(opencodeGitignorePath) ? await deps.readFile(opencodeGitignorePath) ?? "" : "";
19659
20205
  const opencodeGitignore = buildOpencodeGitignore(existingGitignore);
@@ -19673,11 +20219,6 @@ async function runSetup(deps) {
19673
20219
  if (await deps.fileExists(agentPath)) {
19674
20220
  existing.add(ORCHESTRATOR_AGENT_PATH);
19675
20221
  }
19676
- for (const roleAgent of roleAgentFiles) {
19677
- if (await deps.fileExists(roleAgent.path)) {
19678
- existing.add(roleAgent.path);
19679
- }
19680
- }
19681
20222
  for (const command of slashCommands) {
19682
20223
  if (await deps.fileExists(command.path)) {
19683
20224
  existing.add(command.path);
@@ -19720,14 +20261,15 @@ async function runSetup(deps) {
19720
20261
  stdout: shouldWriteOpencodeGitignore ? `setup cancelled by user; ${OPENCODE_GITIGNORE_PATH} ensured` : "setup cancelled by user"
19721
20262
  };
19722
20263
  }
20264
+ const effectivePlannedOpencode = mergeExistingOpencodeConfigFiles(existingOpencodeFiles, opencodeConfigPaths, opencodeWritePlan);
20265
+ if (effectivePlannedOpencode === undefined)
20266
+ throw new Error(executionSetupMessages.lemonadeEndpointMismatch);
20267
+ await verifyLemonadeBeforeWrite(deps, answers, effectiveOpenTeamConfig, effectivePlannedOpencode);
19723
20268
  await deps.writeFile(openTeamPath, serializeOpenTeamConfig(finalOpenTeamConfig));
19724
20269
  for (const [path2, contents] of opencodeWritePlan) {
19725
- await deps.writeFile(path2, contents);
20270
+ await deps.writeFile(path2, serializeOpencodeConfig(contents));
19726
20271
  }
19727
20272
  await deps.writeFile(agentPath, orchestratorAgent);
19728
- for (const roleAgent of roleAgentFiles) {
19729
- await deps.writeFile(roleAgent.path, roleAgent.contents);
19730
- }
19731
20273
  for (const command of slashCommands) {
19732
20274
  await deps.writeFile(command.path, command.contents);
19733
20275
  }
@@ -19748,8 +20290,6 @@ async function runSetup(deps) {
19748
20290
  opencodePath: writtenOpencodePaths,
19749
20291
  configPath: DEFAULT_CONFIG_PATH,
19750
20292
  agentPath: ORCHESTRATOR_AGENT_PATH,
19751
- agentDir: AGENT_DIR,
19752
- roleAgentCount: roleAgentFiles.length,
19753
20293
  commandDir: OPENTEAM_COMMAND_DIR,
19754
20294
  commandCount: slashCommands.length,
19755
20295
  gitignorePath: OPENCODE_GITIGNORE_PATH
@@ -19758,7 +20298,11 @@ async function runSetup(deps) {
19758
20298
  prompt.outro(executionSetupMessages.done);
19759
20299
  return {
19760
20300
  exitCode: 0,
19761
- stdout: executionSetupMessages.complete(`${writtenOpencodePaths}, ${DEFAULT_CONFIG_PATH}, ${ORCHESTRATOR_AGENT_PATH}, ${AGENT_DIR}/*.md (${roleAgentFiles.length} subagents), ${OPENTEAM_COMMAND_DIR}/*.md, ${OPENCODE_GITIGNORE_PATH}`)
20301
+ stdout: [
20302
+ executionSetupMessages.complete(`${writtenOpencodePaths}, ${DEFAULT_CONFIG_PATH}, ${ORCHESTRATOR_AGENT_PATH}, ${OPENTEAM_COMMAND_DIR}/*.md, ${OPENCODE_GITIGNORE_PATH}`),
20303
+ ...v2Notice === undefined ? [] : [v2Notice]
20304
+ ].join(`
20305
+ `)
19762
20306
  };
19763
20307
  } catch (error) {
19764
20308
  const message = error instanceof Error ? error.message : String(error);
@@ -20413,7 +20957,7 @@ import {
20413
20957
  import { basename as basename3, dirname as dirname8, join as join19, relative as relative2, resolve as resolve11, win32 } from "node:path";
20414
20958
  import { fileURLToPath } from "node:url";
20415
20959
  import { promisify as promisify2 } from "node:util";
20416
- import { z as z24 } from "zod";
20960
+ import { z as z25 } from "zod";
20417
20961
  var NPM_ORIGIN = "https://registry.npmjs.org";
20418
20962
  var NPM_PACKAGE_PATH = "/@jmanuelcorral%2Fopenteam";
20419
20963
  var NPM_LATEST_URL = `${NPM_ORIGIN}${NPM_PACKAGE_PATH}/latest`;
@@ -20432,9 +20976,9 @@ var UPGRADE_BACKUP_DIRECTORY = ".opencode/openteam-local/upgrade-backups";
20432
20976
  function npmVersionUrl(version) {
20433
20977
  return `${NPM_ORIGIN}${NPM_PACKAGE_PATH}/${version}`;
20434
20978
  }
20435
- var RegistryPackageIdentitySchema = z24.object({
20436
- name: z24.string(),
20437
- version: z24.string()
20979
+ var RegistryPackageIdentitySchema = z25.object({
20980
+ name: z25.string(),
20981
+ version: z25.string()
20438
20982
  }).strict();
20439
20983
 
20440
20984
  class TransientFetchError extends Error {
@@ -22093,12 +22637,19 @@ function defaultModelFromOpencode(opencodeConfig) {
22093
22637
  }
22094
22638
  return;
22095
22639
  }
22096
- function liveModelsFromSnapshots(snapshots) {
22640
+ function liveModelsFromSnapshots(snapshots, config) {
22097
22641
  const map = new Map;
22098
22642
  for (const snapshot of snapshots) {
22099
- map.set(snapshot.id, {
22643
+ const runtime = config.local.runtimes.find((runtime2) => runtime2.id === snapshot.id);
22644
+ if (runtime === undefined || !runtime.enabled)
22645
+ continue;
22646
+ const kind = localRuntimeKind(runtime);
22647
+ map.set(runtime.defaultModel.providerID, {
22100
22648
  reachable: snapshot.reachable,
22101
- modelIDs: new Set(snapshot.models.map((model) => model.modelID))
22649
+ modelIDs: new Set(snapshot.models.filter((model) => hasRequiredModelResidency(kind, model)).map((model) => model.modelID)),
22650
+ ...kind === "lemonade" ? {
22651
+ residency: new Map(snapshot.models.map((model) => [model.modelID, model.loaded]))
22652
+ } : {}
22102
22653
  });
22103
22654
  }
22104
22655
  return map;
@@ -22384,7 +22935,7 @@ async function runCli(argv, deps) {
22384
22935
  const agentModels = diagnoseAgentModels(agentFiles, {
22385
22936
  opencodeProviders: parseOpencodeProviders(opencodeConfig),
22386
22937
  localRuntimes: localRuntimeStates(config),
22387
- liveModels: liveModelsFromSnapshots(snapshots),
22938
+ liveModels: liveModelsFromSnapshots(snapshots, config),
22388
22939
  sourceDir: deps.agentDir,
22389
22940
  ...defaultModel !== undefined ? { defaultModel } : {}
22390
22941
  });
@@ -22423,29 +22974,53 @@ async function runCli(argv, deps) {
22423
22974
  const rosterMissing = deps.loadRosterForDoctor !== undefined && roster === undefined && rosterLoadError === undefined;
22424
22975
  const orchestratorAgentName = ORCHESTRATOR_AGENT_PATH.slice(ORCHESTRATOR_AGENT_PATH.lastIndexOf("/") + 1).replace(/\.md$/, "");
22425
22976
  const orchestratorAgentMissing = !agentFiles.some((file) => file.name === orchestratorAgentName);
22977
+ const inventoryNotes = [];
22978
+ const readySnapshots = snapshots.map((snapshot) => {
22979
+ const runtime = config.local.runtimes.find((runtime2) => runtime2.id === snapshot.id);
22980
+ if (runtime === undefined || localRuntimeKind(runtime) !== "lemonade")
22981
+ return snapshot;
22982
+ inventoryNotes.push(executionSetupMessages.lemonadeInventory(runtime.id, runtime.defaultModel.providerID, snapshot.models));
22983
+ return {
22984
+ ...snapshot,
22985
+ models: snapshot.models.filter((model) => hasRequiredModelResidency(localRuntimeKind(runtime), model))
22986
+ };
22987
+ });
22988
+ const residencyNotes = agentModels.flatMap((agent) => {
22989
+ const live = agent.resolution?.live;
22990
+ if (agent.model === undefined || live !== "not-loaded" && live !== "residency-unknown")
22991
+ return [];
22992
+ return [
22993
+ executionSetupMessages.lemonadeAgentResidency(agent.name, `${agent.model.providerID}/${agent.model.modelID}`, live)
22994
+ ];
22995
+ });
22426
22996
  return {
22427
22997
  exitCode: 0,
22428
- stdout: renderDoctor({
22429
- config,
22430
- snapshots,
22431
- telemetryPath,
22432
- telemetryRecords: records.length,
22433
- agentModels,
22434
- opencodeConfigPaths,
22435
- ...opencodeConfig !== undefined ? { opencodeConfig } : {},
22436
- ...queueWait !== undefined ? { queueWait } : {},
22437
- ...rosterAudit !== undefined ? { rosterAudit } : {},
22438
- ...rosterRoleCount !== undefined ? { rosterRoleCount } : {},
22439
- ...rosterLoadError !== undefined ? { rosterLoadError } : {},
22440
- ...rosterMissing ? { rosterMissing } : {},
22441
- ...orchestratorAgentMissing ? { orchestratorAgentMissing } : {},
22442
- ...diagnostics !== undefined && diagnostics.length > 0 ? { diagnostics } : {},
22443
- ...opencodeConfigError !== undefined ? { opencodeConfigError } : {},
22444
- ...cacheEntries !== undefined ? { cacheEntries, cliVersion: deps.version } : {},
22445
- ...legacyLayout !== undefined && legacyLayout.length > 0 ? { legacyLayout } : {},
22446
- ...otelBackend !== undefined ? { otelBackend } : {},
22447
- ...gitignoreCoverage !== undefined ? { gitignore: gitignoreCoverage } : {}
22448
- })
22998
+ stdout: [
22999
+ renderDoctor({
23000
+ config,
23001
+ snapshots: readySnapshots,
23002
+ telemetryPath,
23003
+ telemetryRecords: records.length,
23004
+ agentModels,
23005
+ opencodeConfigPaths,
23006
+ ...opencodeConfig !== undefined ? { opencodeConfig } : {},
23007
+ ...queueWait !== undefined ? { queueWait } : {},
23008
+ ...rosterAudit !== undefined ? { rosterAudit } : {},
23009
+ ...rosterRoleCount !== undefined ? { rosterRoleCount } : {},
23010
+ ...rosterLoadError !== undefined ? { rosterLoadError } : {},
23011
+ ...rosterMissing ? { rosterMissing } : {},
23012
+ ...orchestratorAgentMissing ? { orchestratorAgentMissing } : {},
23013
+ ...diagnostics !== undefined && diagnostics.length > 0 ? { diagnostics } : {},
23014
+ ...opencodeConfigError !== undefined ? { opencodeConfigError } : {},
23015
+ ...cacheEntries !== undefined ? { cacheEntries, cliVersion: deps.version } : {},
23016
+ ...legacyLayout !== undefined && legacyLayout.length > 0 ? { legacyLayout } : {},
23017
+ ...otelBackend !== undefined ? { otelBackend } : {},
23018
+ ...gitignoreCoverage !== undefined ? { gitignore: gitignoreCoverage } : {}
23019
+ }),
23020
+ ...inventoryNotes,
23021
+ ...residencyNotes
23022
+ ].join(`
23023
+ `)
22449
23024
  };
22450
23025
  }
22451
23026
  if (command === "migrate") {
@@ -22562,14 +23137,14 @@ ${HELP}`
22562
23137
  }
22563
23138
 
22564
23139
  // src/orchestrator/roster.ts
22565
- import { z as z25 } from "zod";
22566
- var RosterEntrySchema = z25.object({
22567
- roleID: z25.string().min(1),
22568
- agentName: z25.string().min(1)
23140
+ import { z as z26 } from "zod";
23141
+ var RosterEntrySchema = z26.object({
23142
+ roleID: z26.string().min(1),
23143
+ agentName: z26.string().min(1)
22569
23144
  }).strict();
22570
- var RosterSchema = z25.object({
22571
- universe: z25.string().min(1),
22572
- entries: z25.array(RosterEntrySchema)
23145
+ var RosterSchema = z26.object({
23146
+ universe: z26.string().min(1),
23147
+ entries: z26.array(RosterEntrySchema)
22573
23148
  }).strict();
22574
23149
  var ROSTER_JSON_FENCE = /```json\s*([\s\S]*?)```/;
22575
23150
 
@@ -23123,7 +23698,7 @@ import { readFile as readFile4 } from "node:fs/promises";
23123
23698
  import { basename as basename4, dirname as dirname9 } from "node:path";
23124
23699
 
23125
23700
  // src/graph/certificate.ts
23126
- import { z as z26 } from "zod";
23701
+ import { z as z27 } from "zod";
23127
23702
 
23128
23703
  // src/contract/opencode.ts
23129
23704
  var SUPPORTED_OPENCODE_VERSIONS = [
@@ -23164,7 +23739,7 @@ function isCertificateVersionCompatible(certifiedVersion, liveVersion) {
23164
23739
 
23165
23740
  // src/graph/certificate.ts
23166
23741
  var SHADOW_CERTIFICATE_MIN_RUNS = 1000;
23167
- var DivergenceCodeSchema = z26.enum([
23742
+ var DivergenceCodeSchema = z27.enum([
23168
23743
  "status",
23169
23744
  "order",
23170
23745
  "roles",
@@ -23172,50 +23747,50 @@ var DivergenceCodeSchema = z26.enum([
23172
23747
  "fixes",
23173
23748
  "outcome"
23174
23749
  ]);
23175
- var ProvenanceSchema = z26.enum([
23750
+ var ProvenanceSchema = z27.enum([
23176
23751
  "recomputed",
23177
23752
  "compiled",
23178
23753
  "validated",
23179
23754
  "carried"
23180
23755
  ]);
23181
- var EvidenceSchema = z26.object({
23182
- opencodeVersion: z26.string().min(1),
23183
- parity: z26.object({
23184
- runs: z26.number().int().nonnegative(),
23185
- match: z26.number().int().nonnegative(),
23186
- divergent: z26.number().int().nonnegative(),
23187
- inconclusive: z26.number().int().nonnegative(),
23188
- divergences: z26.array(DivergenceCodeSchema)
23756
+ var EvidenceSchema = z27.object({
23757
+ opencodeVersion: z27.string().min(1),
23758
+ parity: z27.object({
23759
+ runs: z27.number().int().nonnegative(),
23760
+ match: z27.number().int().nonnegative(),
23761
+ divergent: z27.number().int().nonnegative(),
23762
+ inconclusive: z27.number().int().nonnegative(),
23763
+ divergences: z27.array(DivergenceCodeSchema)
23189
23764
  }).strict(),
23190
- legacy: z26.object({
23191
- observed: z26.number().int().nonnegative(),
23192
- duplicated: z26.number().int().nonnegative()
23765
+ legacy: z27.object({
23766
+ observed: z27.number().int().nonnegative(),
23767
+ duplicated: z27.number().int().nonnegative()
23193
23768
  }).strict(),
23194
- privacy: z26.object({
23195
- surfacesScanned: z26.number().int().nonnegative(),
23196
- rawFindings: z26.number().int().nonnegative()
23769
+ privacy: z27.object({
23770
+ surfacesScanned: z27.number().int().nonnegative(),
23771
+ rawFindings: z27.number().int().nonnegative()
23197
23772
  }).strict(),
23198
- overhead: z26.object({
23199
- p95Millis: z26.number().nonnegative(),
23200
- budgetMillis: z26.number().positive(),
23201
- samples: z26.number().int().nonnegative()
23773
+ overhead: z27.object({
23774
+ p95Millis: z27.number().nonnegative(),
23775
+ budgetMillis: z27.number().positive(),
23776
+ samples: z27.number().int().nonnegative()
23202
23777
  }).strict(),
23203
- provenance: z26.record(DivergenceCodeSchema, ProvenanceSchema)
23778
+ provenance: z27.record(DivergenceCodeSchema, ProvenanceSchema)
23204
23779
  }).strict();
23205
- var CertificateSchema = z26.object({
23206
- version: z26.literal(1),
23207
- certificate: z26.literal("graph-shadow"),
23208
- opencodeVersion: z26.string().min(1),
23780
+ var CertificateSchema = z27.object({
23781
+ version: z27.literal(1),
23782
+ certificate: z27.literal("graph-shadow"),
23783
+ opencodeVersion: z27.string().min(1),
23209
23784
  evidence: EvidenceSchema,
23210
- verdict: z26.enum(["pass", "fail"]),
23211
- failedGates: z26.array(z26.enum([
23785
+ verdict: z27.enum(["pass", "fail"]),
23786
+ failedGates: z27.array(z27.enum([
23212
23787
  "sample",
23213
23788
  "parity",
23214
23789
  "one-legacy-execution",
23215
23790
  "privacy",
23216
23791
  "overhead"
23217
23792
  ])),
23218
- digest: z26.string().regex(/^[0-9a-f]{64}$/)
23793
+ digest: z27.string().regex(/^[0-9a-f]{64}$/)
23219
23794
  }).strict();
23220
23795
 
23221
23796
  class ShadowCertificateError extends Error {
@@ -23330,37 +23905,37 @@ function parseShadowCertificate(value, opencodeVersion) {
23330
23905
  }
23331
23906
  return certificate;
23332
23907
  }
23333
- var ReleaseDeterminismSchema = z26.object({ runs: z26.number().int().nonnegative(), allMatch: z26.boolean() }).strict();
23334
- var ReleaseCrashRecoverySchema = z26.object({
23335
- scenarios: z26.number().int().nonnegative(),
23336
- allConverge: z26.boolean()
23908
+ var ReleaseDeterminismSchema = z27.object({ runs: z27.number().int().nonnegative(), allMatch: z27.boolean() }).strict();
23909
+ var ReleaseCrashRecoverySchema = z27.object({
23910
+ scenarios: z27.number().int().nonnegative(),
23911
+ allConverge: z27.boolean()
23337
23912
  }).strict();
23338
- var ReleaseReplayEquivalenceSchema = z26.object({
23339
- checks: z26.number().int().nonnegative(),
23340
- allEquivalent: z26.boolean()
23913
+ var ReleaseReplayEquivalenceSchema = z27.object({
23914
+ checks: z27.number().int().nonnegative(),
23915
+ allEquivalent: z27.boolean()
23341
23916
  }).strict();
23342
- var ReleasePrivacySchema = z26.object({
23343
- surfacesScanned: z26.number().int().nonnegative(),
23344
- rawFindings: z26.number().int().nonnegative()
23917
+ var ReleasePrivacySchema = z27.object({
23918
+ surfacesScanned: z27.number().int().nonnegative(),
23919
+ rawFindings: z27.number().int().nonnegative()
23345
23920
  }).strict();
23346
- var ReleaseContractSchema = z26.object({
23347
- roundTrips: z26.number().int().nonnegative(),
23348
- allSettled: z26.boolean()
23921
+ var ReleaseContractSchema = z27.object({
23922
+ roundTrips: z27.number().int().nonnegative(),
23923
+ allSettled: z27.boolean()
23349
23924
  }).strict();
23350
- var ReleaseHealthSchema = z26.object({
23351
- samples: z26.number().int().nonnegative(),
23352
- bounded: z26.boolean(),
23353
- idempotent: z26.boolean()
23925
+ var ReleaseHealthSchema = z27.object({
23926
+ samples: z27.number().int().nonnegative(),
23927
+ bounded: z27.boolean(),
23928
+ idempotent: z27.boolean()
23354
23929
  }).strict();
23355
- var ReleasePerformanceSchema = z26.object({
23356
- p95Millis: z26.number().nonnegative(),
23357
- budgetMillis: z26.number().positive(),
23358
- samples: z26.number().int().nonnegative()
23930
+ var ReleasePerformanceSchema = z27.object({
23931
+ p95Millis: z27.number().nonnegative(),
23932
+ budgetMillis: z27.number().positive(),
23933
+ samples: z27.number().int().nonnegative()
23359
23934
  }).strict();
23360
- var ReleaseEvidenceSchema = z26.object({
23361
- opencodeVersion: z26.string().min(1),
23362
- platform: z26.enum(["linux", "win32"]),
23363
- shadowCertificateDigest: z26.string().regex(/^[0-9a-f]{64}$/),
23935
+ var ReleaseEvidenceSchema = z27.object({
23936
+ opencodeVersion: z27.string().min(1),
23937
+ platform: z27.enum(["linux", "win32"]),
23938
+ shadowCertificateDigest: z27.string().regex(/^[0-9a-f]{64}$/),
23364
23939
  determinism: ReleaseDeterminismSchema,
23365
23940
  crashRecovery: ReleaseCrashRecoverySchema,
23366
23941
  replayEquivalence: ReleaseReplayEquivalenceSchema,
@@ -23369,15 +23944,15 @@ var ReleaseEvidenceSchema = z26.object({
23369
23944
  health: ReleaseHealthSchema,
23370
23945
  performance: ReleasePerformanceSchema
23371
23946
  }).strict();
23372
- var ReleaseCertificateSchema = z26.object({
23373
- version: z26.literal(2),
23374
- certificate: z26.literal("graph-release"),
23375
- platform: z26.enum(["linux", "win32"]),
23376
- opencodeVersion: z26.string().min(1),
23377
- shadowCertificateDigest: z26.string().regex(/^[0-9a-f]{64}$/),
23947
+ var ReleaseCertificateSchema = z27.object({
23948
+ version: z27.literal(2),
23949
+ certificate: z27.literal("graph-release"),
23950
+ platform: z27.enum(["linux", "win32"]),
23951
+ opencodeVersion: z27.string().min(1),
23952
+ shadowCertificateDigest: z27.string().regex(/^[0-9a-f]{64}$/),
23378
23953
  evidence: ReleaseEvidenceSchema,
23379
- verdict: z26.enum(["pass", "fail"]),
23380
- failedGates: z26.array(z26.enum([
23954
+ verdict: z27.enum(["pass", "fail"]),
23955
+ failedGates: z27.array(z27.enum([
23381
23956
  "shadow-valid",
23382
23957
  "determinism",
23383
23958
  "crash-recovery",
@@ -23387,7 +23962,7 @@ var ReleaseCertificateSchema = z26.object({
23387
23962
  "health",
23388
23963
  "performance"
23389
23964
  ])),
23390
- digest: z26.string().regex(/^[0-9a-f]{64}$/)
23965
+ digest: z27.string().regex(/^[0-9a-f]{64}$/)
23391
23966
  }).strict();
23392
23967
 
23393
23968
  class ReleaseCertificateError extends Error {
@@ -23515,7 +24090,7 @@ function parseReleaseCertificate(value, opencodeVersion, expectedShadowDigest) {
23515
24090
  }
23516
24091
  return certificate;
23517
24092
  }
23518
- var ShadowDivergenceCodeSchema = z26.enum([
24093
+ var ShadowDivergenceCodeSchema = z27.enum([
23519
24094
  "status",
23520
24095
  "order",
23521
24096
  "roles",
@@ -23524,13 +24099,13 @@ var ShadowDivergenceCodeSchema = z26.enum([
23524
24099
  "outcome"
23525
24100
  ]);
23526
24101
  var RECOMPUTED_DIVERGENCE_CODES = ["fixes"];
23527
- var RecomputedDivergenceCodeSchema = z26.enum(RECOMPUTED_DIVERGENCE_CODES);
23528
- var SoakPolicySchema = z26.object({
23529
- minimumGenuineObservations: z26.number().int().positive(),
23530
- minimumDistinctDays: z26.number().int().positive(),
23531
- requireGenuineUsageOnAllPlatforms: z26.boolean(),
23532
- supportedOpencodeVersions: z26.array(z26.string().min(1)).min(1),
23533
- criticalDivergenceCodes: z26.array(RecomputedDivergenceCodeSchema).min(1)
24102
+ var RecomputedDivergenceCodeSchema = z27.enum(RECOMPUTED_DIVERGENCE_CODES);
24103
+ var SoakPolicySchema = z27.object({
24104
+ minimumGenuineObservations: z27.number().int().positive(),
24105
+ minimumDistinctDays: z27.number().int().positive(),
24106
+ requireGenuineUsageOnAllPlatforms: z27.boolean(),
24107
+ supportedOpencodeVersions: z27.array(z27.string().min(1)).min(1),
24108
+ criticalDivergenceCodes: z27.array(RecomputedDivergenceCodeSchema).min(1)
23534
24109
  }).strict();
23535
24110
  var DEFAULT_SOAK_POLICY = {
23536
24111
  minimumGenuineObservations: 100,
@@ -23539,46 +24114,46 @@ var DEFAULT_SOAK_POLICY = {
23539
24114
  supportedOpencodeVersions: [...SUPPORTED_OPENCODE_VERSIONS],
23540
24115
  criticalDivergenceCodes: ["fixes"]
23541
24116
  };
23542
- var SoakPlatformEnumSchema = z26.enum(["linux", "win32", "darwin"]);
23543
- var SoakPrivacyPairEvidenceSchema = z26.object({
23544
- surfacesScanned: z26.number().int().nonnegative(),
23545
- rawFindings: z26.number().int().nonnegative()
24117
+ var SoakPlatformEnumSchema = z27.enum(["linux", "win32", "darwin"]);
24118
+ var SoakPrivacyPairEvidenceSchema = z27.object({
24119
+ surfacesScanned: z27.number().int().nonnegative(),
24120
+ rawFindings: z27.number().int().nonnegative()
23546
24121
  }).strict();
23547
- var SoakDuplicateEffectPairEvidenceSchema = z26.object({
23548
- effectsExamined: z26.number().int().nonnegative(),
23549
- duplicatesFound: z26.number().int().nonnegative()
24122
+ var SoakDuplicateEffectPairEvidenceSchema = z27.object({
24123
+ effectsExamined: z27.number().int().nonnegative(),
24124
+ duplicatesFound: z27.number().int().nonnegative()
23550
24125
  }).strict();
23551
- var SoakModelVerificationPairEvidenceSchema = z26.object({
23552
- nodesChecked: z26.number().int().nonnegative(),
23553
- unverified: z26.number().int().nonnegative(),
23554
- mismatched: z26.number().int().nonnegative()
24126
+ var SoakModelVerificationPairEvidenceSchema = z27.object({
24127
+ nodesChecked: z27.number().int().nonnegative(),
24128
+ unverified: z27.number().int().nonnegative(),
24129
+ mismatched: z27.number().int().nonnegative()
23555
24130
  }).strict();
23556
- var SoakEvidenceSchema = z26.object({
23557
- totalObservations: z26.number().int().nonnegative(),
23558
- genuineUsageObservations: z26.number().int().nonnegative(),
23559
- ciSyntheticObservations: z26.number().int().nonnegative(),
23560
- recorderCount: z26.number().int().nonnegative(),
23561
- ineligibleObservations: z26.number().int().nonnegative(),
23562
- firstTimestamp: z26.string().datetime().nullable(),
23563
- lastTimestamp: z26.string().datetime().nullable(),
23564
- distinctDays: z26.number().int().nonnegative(),
23565
- criticalDivergences: z26.number().int().nonnegative().nullable(),
24131
+ var SoakEvidenceSchema = z27.object({
24132
+ totalObservations: z27.number().int().nonnegative(),
24133
+ genuineUsageObservations: z27.number().int().nonnegative(),
24134
+ ciSyntheticObservations: z27.number().int().nonnegative(),
24135
+ recorderCount: z27.number().int().nonnegative(),
24136
+ ineligibleObservations: z27.number().int().nonnegative(),
24137
+ firstTimestamp: z27.string().datetime().nullable(),
24138
+ lastTimestamp: z27.string().datetime().nullable(),
24139
+ distinctDays: z27.number().int().nonnegative(),
24140
+ criticalDivergences: z27.number().int().nonnegative().nullable(),
23566
24141
  privacy: SoakPrivacyPairEvidenceSchema.nullable(),
23567
24142
  duplicateEffects: SoakDuplicateEffectPairEvidenceSchema.nullable(),
23568
24143
  modelVerification: SoakModelVerificationPairEvidenceSchema.nullable(),
23569
- platformsGenuine: z26.array(SoakPlatformEnumSchema),
23570
- platformsSynthetic: z26.array(SoakPlatformEnumSchema),
23571
- opencodeVersionsObserved: z26.array(z26.string().min(1)),
23572
- invalidChains: z26.number().int().nonnegative()
24144
+ platformsGenuine: z27.array(SoakPlatformEnumSchema),
24145
+ platformsSynthetic: z27.array(SoakPlatformEnumSchema),
24146
+ opencodeVersionsObserved: z27.array(z27.string().min(1)),
24147
+ invalidChains: z27.number().int().nonnegative()
23573
24148
  }).strict();
23574
- var SoakCertificateSchema = z26.object({
23575
- version: z26.literal(1),
23576
- certificate: z26.literal("graph-soak"),
23577
- trustBoundary: z26.literal("contributors"),
24149
+ var SoakCertificateSchema = z27.object({
24150
+ version: z27.literal(1),
24151
+ certificate: z27.literal("graph-soak"),
24152
+ trustBoundary: z27.literal("contributors"),
23578
24153
  policy: SoakPolicySchema,
23579
24154
  evidence: SoakEvidenceSchema,
23580
- verdict: z26.enum(["pass", "fail"]),
23581
- failedGates: z26.array(z26.enum([
24155
+ verdict: z27.enum(["pass", "fail"]),
24156
+ failedGates: z27.array(z27.enum([
23582
24157
  "minimum-observations",
23583
24158
  "minimum-duration",
23584
24159
  "chain-integrity",
@@ -23589,8 +24164,8 @@ var SoakCertificateSchema = z26.object({
23589
24164
  "os-diversity",
23590
24165
  "opencode-pin"
23591
24166
  ])),
23592
- ledgerDigest: z26.string().regex(/^[0-9a-f]{64}$/),
23593
- digest: z26.string().regex(/^[0-9a-f]{64}$/)
24167
+ ledgerDigest: z27.string().regex(/^[0-9a-f]{64}$/),
24168
+ digest: z27.string().regex(/^[0-9a-f]{64}$/)
23594
24169
  }).strict();
23595
24170
 
23596
24171
  class SoakCertificateError extends Error {
@@ -24238,62 +24813,62 @@ var realCacheAdapter = {
24238
24813
  };
24239
24814
 
24240
24815
  // src/memory/types.ts
24241
- import { z as z27 } from "zod";
24816
+ import { z as z28 } from "zod";
24242
24817
  var SHARED_OWNER_KEY = "*";
24243
- var OwnerKeySchema = z27.string().min(1);
24244
- var MemoryKindSchema = z27.enum(["fact", "preference", "entity"]);
24245
- var MemoryBaseSchema = z27.object({
24246
- id: z27.string().min(1),
24818
+ var OwnerKeySchema = z28.string().min(1);
24819
+ var MemoryKindSchema = z28.enum(["fact", "preference", "entity"]);
24820
+ var MemoryBaseSchema = z28.object({
24821
+ id: z28.string().min(1),
24247
24822
  ownerKey: OwnerKeySchema,
24248
- confidence: z27.number().min(0).max(1),
24249
- validFrom: z27.number().finite(),
24250
- validUntil: z27.number().finite().nullable().default(null),
24251
- createdAt: z27.number().finite(),
24252
- invalidatedAt: z27.number().finite().nullable().default(null),
24253
- sourceHash: z27.string().min(1),
24254
- supersededBy: z27.string().min(1).nullable().default(null)
24823
+ confidence: z28.number().min(0).max(1),
24824
+ validFrom: z28.number().finite(),
24825
+ validUntil: z28.number().finite().nullable().default(null),
24826
+ createdAt: z28.number().finite(),
24827
+ invalidatedAt: z28.number().finite().nullable().default(null),
24828
+ sourceHash: z28.string().min(1),
24829
+ supersededBy: z28.string().min(1).nullable().default(null)
24255
24830
  }).strict();
24256
24831
  var FactSchema = MemoryBaseSchema.extend({
24257
- kind: z27.literal("fact"),
24258
- subject: z27.string().min(1),
24259
- predicate: z27.string().min(1),
24260
- object: z27.string().min(1),
24261
- category: z27.string().min(1).nullable().default(null)
24832
+ kind: z28.literal("fact"),
24833
+ subject: z28.string().min(1),
24834
+ predicate: z28.string().min(1),
24835
+ object: z28.string().min(1),
24836
+ category: z28.string().min(1).nullable().default(null)
24262
24837
  });
24263
24838
  var PreferenceSchema = MemoryBaseSchema.extend({
24264
- kind: z27.literal("preference"),
24265
- category: z27.string().min(1),
24266
- preference: z27.string().min(1),
24267
- context: z27.string().min(1).nullable().default(null),
24268
- lastAccessedAt: z27.number().finite().nullable().default(null),
24269
- accessCount: z27.number().int().min(0).default(0)
24839
+ kind: z28.literal("preference"),
24840
+ category: z28.string().min(1),
24841
+ preference: z28.string().min(1),
24842
+ context: z28.string().min(1).nullable().default(null),
24843
+ lastAccessedAt: z28.number().finite().nullable().default(null),
24844
+ accessCount: z28.number().int().min(0).default(0)
24270
24845
  });
24271
24846
  var EntitySchema = MemoryBaseSchema.extend({
24272
- kind: z27.literal("entity"),
24273
- canonicalName: z27.string().min(1),
24274
- type: z27.string().min(1),
24275
- aliases: z27.array(z27.string().min(1)).default([])
24847
+ kind: z28.literal("entity"),
24848
+ canonicalName: z28.string().min(1),
24849
+ type: z28.string().min(1),
24850
+ aliases: z28.array(z28.string().min(1)).default([])
24276
24851
  });
24277
24852
  var RelationSchema = MemoryBaseSchema.extend({
24278
- kind: z27.literal("relation"),
24279
- from: z27.string().min(1),
24280
- to: z27.string().min(1),
24281
- predicate: z27.string().min(1),
24282
- annotation: z27.string().min(1).nullable().default(null)
24853
+ kind: z28.literal("relation"),
24854
+ from: z28.string().min(1),
24855
+ to: z28.string().min(1),
24856
+ predicate: z28.string().min(1),
24857
+ annotation: z28.string().min(1).nullable().default(null)
24283
24858
  });
24284
- var MemoryRecordSchema = z27.discriminatedUnion("kind", [
24859
+ var MemoryRecordSchema = z28.discriminatedUnion("kind", [
24285
24860
  FactSchema,
24286
24861
  PreferenceSchema,
24287
24862
  EntitySchema,
24288
24863
  RelationSchema
24289
24864
  ]);
24290
- var RecallQuerySchema = z27.object({
24865
+ var RecallQuerySchema = z28.object({
24291
24866
  ownerKey: OwnerKeySchema,
24292
- kinds: z27.array(MemoryKindSchema).default(["fact", "preference", "entity"]),
24293
- asOf: z27.number().finite().nullable().default(null),
24294
- limit: z27.number().int().positive().default(8),
24295
- minSimilarity: z27.number().min(0).max(1).default(0.2),
24296
- includeShared: z27.boolean().default(true)
24867
+ kinds: z28.array(MemoryKindSchema).default(["fact", "preference", "entity"]),
24868
+ asOf: z28.number().finite().nullable().default(null),
24869
+ limit: z28.number().int().positive().default(8),
24870
+ minSimilarity: z28.number().min(0).max(1).default(0.2),
24871
+ includeShared: z28.boolean().default(true)
24297
24872
  }).strict();
24298
24873
 
24299
24874
  // src/memory/rank.ts
@@ -25714,7 +26289,7 @@ function createGitLastCommit(exec) {
25714
26289
  // src/cli.ts
25715
26290
  var execFileAsync3 = promisify3(execFile3);
25716
26291
  var execAsync = promisify3(exec);
25717
- var AGENT_DIR2 = dirname10(ORCHESTRATOR_AGENT_PATH);
26292
+ var AGENT_DIR = dirname10(ORCHESTRATOR_AGENT_PATH);
25718
26293
  var storage = createFsStorageProvider(process.cwd());
25719
26294
  var CLI_LIFECYCLE_WRITER_IDENTITY = {
25720
26295
  processID: process.pid,
@@ -25959,7 +26534,7 @@ var deps = {
25959
26534
  telemetryPath: DEFAULT_TELEMETRY_PATH,
25960
26535
  opencodeConfigPaths: OPENCODE_CONFIG_CANDIDATES,
25961
26536
  orchestratorAgentPath: ORCHESTRATOR_AGENT_PATH,
25962
- agentDir: AGENT_DIR2,
26537
+ agentDir: AGENT_DIR,
25963
26538
  cachePort: realCacheAdapter,
25964
26539
  purge: createCliPurgeRuntime(),
25965
26540
  loadRosterForDoctor: readStoredRosterForDoctor,
@@ -26092,7 +26667,7 @@ async function main() {
26092
26667
  sessionsDir: effectiveSessionsDir,
26093
26668
  decisionsPath: resolveReadPathForCurrent(DEFAULT_DECISIONS_PATH, legacyExists),
26094
26669
  backlogPath: resolveReadPathForCurrent(DEFAULT_BACKLOG_PATH, legacyExists),
26095
- agentDir: AGENT_DIR2,
26670
+ agentDir: AGENT_DIR,
26096
26671
  legacyTelemetryPath: effectiveTelemetryPath,
26097
26672
  storage,
26098
26673
  listAgentFiles: deps.listAgentFiles,