@jmanuelcorral/openteam 0.16.0 → 0.16.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -3,9 +3,10 @@ import { createRequire } from "node:module";
3
3
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
4
4
 
5
5
  // src/cli.ts
6
- import { execFile as execFile2 } from "node:child_process";
7
- import { mkdir as mkdir2, readFile as readFile2, writeFile as writeFile2 } from "node:fs/promises";
8
- import { dirname as dirname5, resolve as resolve6 } from "node:path";
6
+ import { exec, execFile as execFile2 } from "node:child_process";
7
+ import { mkdir as mkdir2, readFile as readFile3, writeFile as writeFile2 } from "node:fs/promises";
8
+ import { createRequire as createRequire2 } from "node:module";
9
+ import { dirname as dirname6, resolve as resolve6 } from "node:path";
9
10
  import { promisify as promisify2 } from "node:util";
10
11
  import {
11
12
  confirm as clackConfirm,
@@ -8551,7 +8552,15 @@ function recoverJournal(spec, text) {
8551
8552
  }
8552
8553
 
8553
8554
  // src/cli/graphOperator.ts
8554
- var ALLOWED_GATE = { allowed: true, violations: [] };
8555
+ var ALLOWED_GATE = {
8556
+ allowed: true,
8557
+ violations: [],
8558
+ advisories: []
8559
+ };
8560
+ var ALLOWED_EVALUATION = {
8561
+ kind: "evaluated",
8562
+ gate: ALLOWED_GATE
8563
+ };
8555
8564
  function journalRoot(config) {
8556
8565
  return config.graph?.journalRoot ?? DEFAULT_GRAPH_JOURNAL_ROOT;
8557
8566
  }
@@ -8641,7 +8650,7 @@ async function listWorktreeObservations(config, options) {
8641
8650
  })));
8642
8651
  }
8643
8652
  function createGraphOperator(options) {
8644
- const evaluateCutoverGate = options.evaluateCutoverGate ?? (async () => ALLOWED_GATE);
8653
+ const evaluateCutoverGate = options.evaluateCutoverGate ?? (async () => ALLOWED_EVALUATION);
8645
8654
  const worktreeOptions = options.exec === undefined ? undefined : {
8646
8655
  exec: options.exec,
8647
8656
  repoRoot: options.repoRoot ?? process.cwd(),
@@ -8920,6 +8929,35 @@ function createMigrateAdapter(root) {
8920
8929
  };
8921
8930
  }
8922
8931
 
8932
+ // src/cli/opencodeVersion.ts
8933
+ var SEMVER_PATTERN = /(\d+\.\d+\.\d+)/;
8934
+ function normalizeVersion(raw) {
8935
+ if (raw === undefined) {
8936
+ return;
8937
+ }
8938
+ const trimmed = raw.trim();
8939
+ if (trimmed === "" || trimmed.toLowerCase() === "unknown") {
8940
+ return;
8941
+ }
8942
+ const match = SEMVER_PATTERN.exec(trimmed);
8943
+ return match?.[1] ?? undefined;
8944
+ }
8945
+ async function resolveOpencodeVersionBestEffort(deps) {
8946
+ try {
8947
+ const fromPackage = normalizeVersion(deps.readPackageVersion("opencode-ai"));
8948
+ if (fromPackage !== undefined) {
8949
+ return fromPackage;
8950
+ }
8951
+ } catch {}
8952
+ try {
8953
+ const fromCommand = normalizeVersion(await deps.runVersionCommand());
8954
+ if (fromCommand !== undefined) {
8955
+ return fromCommand;
8956
+ }
8957
+ } catch {}
8958
+ return;
8959
+ }
8960
+
8923
8961
  // src/cli/purgeAdapter.ts
8924
8962
  import { execFile } from "node:child_process";
8925
8963
  import * as fs2 from "node:fs";
@@ -10239,6 +10277,56 @@ function createProbeRuntime(exec) {
10239
10277
  }
10240
10278
 
10241
10279
  // src/config/graphFeatureGate.ts
10280
+ function checkCertificate(status, name) {
10281
+ if (status.status === "absent") {
10282
+ return {
10283
+ code: `${name}-certificate-absent`,
10284
+ detail: `${name} certificate not found — run the ${name} certification suite to produce it`
10285
+ };
10286
+ }
10287
+ if (status.status === "invalid") {
10288
+ return {
10289
+ code: `${name}-certificate-invalid`,
10290
+ detail: `${name} certificate rejected by recomputing parser: ${status.code} — ${status.detail}`
10291
+ };
10292
+ }
10293
+ return;
10294
+ }
10295
+ function evaluateGraphGate(input) {
10296
+ const violations = [];
10297
+ const advisories = [];
10298
+ const shadowV = checkCertificate(input.shadow, "shadow");
10299
+ if (shadowV !== undefined)
10300
+ violations.push(shadowV);
10301
+ const releaseV = checkCertificate(input.release, "release");
10302
+ if (releaseV !== undefined)
10303
+ violations.push(releaseV);
10304
+ const soakV = checkCertificate(input.soak, "soak");
10305
+ if (soakV !== undefined)
10306
+ advisories.push(soakV);
10307
+ if (input.migration === "pending") {
10308
+ violations.push({
10309
+ code: "migration-pending",
10310
+ detail: "legacy migration has pending work — run the migration tool to completion before enabling active mode"
10311
+ });
10312
+ } else if (input.migration === "unknown") {
10313
+ violations.push({
10314
+ code: "migration-unknown",
10315
+ detail: "could not determine migration status — check ledger readability and storage access"
10316
+ });
10317
+ }
10318
+ if (!input.operatorApproval) {
10319
+ violations.push({
10320
+ code: "operator-approval-missing",
10321
+ detail: "set graph.operatorApproval to true in .opencode/openteam.json after reviewing all certificates and completing migration — this is a declaration of intent, not a proof"
10322
+ });
10323
+ }
10324
+ return {
10325
+ allowed: violations.length === 0,
10326
+ violations,
10327
+ advisories
10328
+ };
10329
+ }
10242
10330
  function isGateApplicable(mode) {
10243
10331
  return mode === "active";
10244
10332
  }
@@ -11057,7 +11145,7 @@ function legacyRosterFindings(audit) {
11057
11145
  }
11058
11146
  return findings;
11059
11147
  }
11060
- function rosterHealthSection(audit, loadError, rosterRoleCount) {
11148
+ function rosterHealthSection(audit, loadError, rosterRoleCount, rosterMissing) {
11061
11149
  const auditFindings = audit?.findings ?? legacyRosterFindings(audit ?? {});
11062
11150
  const unparseable = auditFindings.find((finding) => finding.kind === "unparseable-roster");
11063
11151
  const unparseableMessage = loadError ?? unparseable?.message;
@@ -11070,6 +11158,14 @@ function rosterHealthSection(audit, loadError, rosterRoleCount) {
11070
11158
  ];
11071
11159
  }
11072
11160
  if (audit === undefined) {
11161
+ if (rosterMissing === true) {
11162
+ return [
11163
+ " roster health:",
11164
+ ` ✗ ${OPENTEAM_ROSTER_PATH} does not exist — no team roster has been cast.`,
11165
+ " consequence: without a roster openteam cannot distribute work across roles, so every request is handled by a single agent and no work splitting happens.",
11166
+ ` remedy: run \`openteam setup\` to cast a team and write the roster (${OPENTEAM_ROSTER_PATH}), then re-run \`openteam doctor\`.`
11167
+ ];
11168
+ }
11073
11169
  return [];
11074
11170
  }
11075
11171
  const missing = auditFindings.filter((finding) => finding.kind === "missing-guaranteed-role").map((finding) => finding.roleID);
@@ -11105,6 +11201,14 @@ function rosterHealthSection(audit, loadError, rosterRoleCount) {
11105
11201
  }
11106
11202
  return lines;
11107
11203
  }
11204
+ function orchestratorAgentSection() {
11205
+ return [
11206
+ " orchestrator agent:",
11207
+ ` ✗ ${ORCHESTRATOR_AGENT_PATH} does not exist — opencode has no openteam coordinator agent.`,
11208
+ " consequence: this is the primary agent that reads the roster and calls `openteam-orchestrate` to fan work out across roles; without it opencode has no openteam coordinator, so every request is handled by a single agent and no work splitting happens.",
11209
+ ` remedy: run \`openteam setup\` to generate ${ORCHESTRATOR_AGENT_PATH}, then re-run \`openteam doctor\`.`
11210
+ ];
11211
+ }
11108
11212
  function gitignoreSection(gitignore) {
11109
11213
  if (gitignore.missingRules.length === 0) {
11110
11214
  return [
@@ -11168,7 +11272,10 @@ function renderDoctor(input) {
11168
11272
  if (input.diagnostics !== undefined) {
11169
11273
  lines.push(...diagnosticsSection(input.diagnostics));
11170
11274
  }
11171
- lines.push(...rosterHealthSection(input.rosterAudit, input.rosterLoadError, input.rosterRoleCount));
11275
+ lines.push(...rosterHealthSection(input.rosterAudit, input.rosterLoadError, input.rosterRoleCount, input.rosterMissing));
11276
+ if (input.orchestratorAgentMissing === true) {
11277
+ lines.push(...orchestratorAgentSection());
11278
+ }
11172
11279
  if (input.gitignore !== undefined) {
11173
11280
  lines.push(...gitignoreSection(input.gitignore));
11174
11281
  }
@@ -11237,14 +11344,38 @@ function leaseLine(lease) {
11237
11344
  const state = lease.released ? "released" : "active";
11238
11345
  return ` lease: ${lease.holder} → ${lease.runID} (${state})`;
11239
11346
  }
11240
- function renderGraphStatus(snapshot) {
11347
+ function appendAdvisoryLines(lines, advisories) {
11348
+ for (const advisory of advisories ?? []) {
11349
+ lines.push(` ⚠ advisory (non-blocking) ${advisory.code}: ${advisory.detail}`);
11350
+ }
11351
+ }
11352
+ function appendEffectiveLines(lines, gate) {
11353
+ switch (gate.kind) {
11354
+ case "not-applicable":
11355
+ return;
11356
+ case "passed":
11357
+ lines.push(" effective: active");
11358
+ appendAdvisoryLines(lines, gate.advisories);
11359
+ return;
11360
+ case "unevaluable":
11361
+ lines.push(" effective: unknown — could not evaluate the cutover gate");
11362
+ lines.push(` ${gate.reason}`);
11363
+ return;
11364
+ case "denied":
11365
+ lines.push(" effective: off — cutover gate denied");
11366
+ for (const violation of gate.violations) {
11367
+ lines.push(` • ${violation.code}: ${violation.detail}`);
11368
+ }
11369
+ appendAdvisoryLines(lines, gate.advisories);
11370
+ }
11371
+ }
11372
+ function renderGraphStatus(snapshot, gate = { kind: "not-applicable" }) {
11241
11373
  const lines = [
11242
11374
  "openteam graph status:",
11243
- ` config: ${snapshot.effectiveMode} (source: ${snapshot.source})`,
11244
- leaseLine(snapshot.lease),
11245
- ` journal: ${snapshot.journal}`,
11246
- ` effects: ${snapshot.pendingEffects} pending, ${snapshot.unknownEffects} unknown`
11375
+ ` config: ${snapshot.effectiveMode} (source: ${snapshot.source})`
11247
11376
  ];
11377
+ appendEffectiveLines(lines, gate);
11378
+ lines.push(leaseLine(snapshot.lease), ` journal: ${snapshot.journal}`, ` effects: ${snapshot.pendingEffects} pending, ${snapshot.unknownEffects} unknown`);
11248
11379
  const needsReconciliation = isRecoveryRequired(snapshot.journal, snapshot.unknownEffects);
11249
11380
  if (needsReconciliation) {
11250
11381
  lines.push(" ⚠ Reconciliation required: journal or effects in an uncertain state.");
@@ -11495,11 +11626,152 @@ function mergeOpencodeConfigs(base, override) {
11495
11626
  return result;
11496
11627
  }
11497
11628
 
11629
+ // src/graph/soakLedger.ts
11630
+ import { z as z19 } from "zod";
11631
+ var Sha256Schema2 = z19.string().regex(/^[0-9a-f]{64}$/);
11632
+ var SoakPlatformSchema = z19.enum(["linux", "win32", "darwin"]);
11633
+ var PrivacyPairSchema = z19.object({
11634
+ surfacesScanned: z19.number().int().nonnegative(),
11635
+ rawFindings: z19.number().int().nonnegative()
11636
+ }).strict();
11637
+ var DuplicateEffectPairSchema = z19.object({
11638
+ effectsExamined: z19.number().int().nonnegative(),
11639
+ duplicatesFound: z19.number().int().nonnegative()
11640
+ }).strict();
11641
+ var ModelVerificationPairSchema = z19.object({
11642
+ nodesChecked: z19.number().int().nonnegative(),
11643
+ unverified: z19.number().int().nonnegative(),
11644
+ mismatched: z19.number().int().nonnegative()
11645
+ }).strict();
11646
+ var SoakObservationSchema = z19.object({
11647
+ version: z19.literal(1),
11648
+ seq: z19.number().int().nonnegative(),
11649
+ timestamp: z19.string().datetime(),
11650
+ platform: SoakPlatformSchema,
11651
+ opencodeVersion: z19.string().min(1),
11652
+ provenance: z19.enum(["genuine-usage", "ci-synthetic"]),
11653
+ traceDigest: Sha256Schema2,
11654
+ criticalDivergences: z19.number().int().nonnegative().nullable(),
11655
+ privacy: PrivacyPairSchema.nullable(),
11656
+ duplicateEffects: DuplicateEffectPairSchema.nullable(),
11657
+ modelVerification: ModelVerificationPairSchema.nullable(),
11658
+ chainDigest: Sha256Schema2
11659
+ }).strict();
11660
+ function canonicalObservationPreimage(obs) {
11661
+ return JSON.stringify({
11662
+ version: obs.version,
11663
+ seq: obs.seq,
11664
+ timestamp: obs.timestamp,
11665
+ platform: obs.platform,
11666
+ opencodeVersion: obs.opencodeVersion,
11667
+ provenance: obs.provenance,
11668
+ traceDigest: obs.traceDigest,
11669
+ criticalDivergences: obs.criticalDivergences,
11670
+ privacy: obs.privacy,
11671
+ duplicateEffects: obs.duplicateEffects,
11672
+ modelVerification: obs.modelVerification
11673
+ });
11674
+ }
11675
+ function validateChain(observations) {
11676
+ if (observations.length === 0) {
11677
+ return { valid: true, observations: [] };
11678
+ }
11679
+ let prevChainDigest = "0".repeat(64);
11680
+ for (let i = 0;i < observations.length; i++) {
11681
+ const obs = observations[i];
11682
+ if (obs.seq !== i) {
11683
+ return { valid: false, observations };
11684
+ }
11685
+ if (i > 0) {
11686
+ const prev = observations[i - 1];
11687
+ if (obs.timestamp <= prev.timestamp) {
11688
+ return { valid: false, observations };
11689
+ }
11690
+ }
11691
+ const preimage = canonicalObservationPreimage(obs);
11692
+ const expectedDigest = sha256Hex(prevChainDigest + preimage);
11693
+ if (obs.chainDigest !== expectedDigest) {
11694
+ return { valid: false, observations };
11695
+ }
11696
+ prevChainDigest = obs.chainDigest;
11697
+ }
11698
+ return { valid: true, observations };
11699
+ }
11700
+ function canonicalLedger(chains) {
11701
+ const validChains = [];
11702
+ for (const chain of chains) {
11703
+ const result = validateChain(chain);
11704
+ if (result.valid && chain.length > 0) {
11705
+ validChains.push(chain);
11706
+ }
11707
+ }
11708
+ validChains.sort((a, b) => {
11709
+ const aDigest = a[0].chainDigest;
11710
+ const bDigest = b[0].chainDigest;
11711
+ return aDigest < bDigest ? -1 : aDigest > bDigest ? 1 : 0;
11712
+ });
11713
+ const lines = [];
11714
+ for (const chain of validChains) {
11715
+ for (const obs of chain) {
11716
+ lines.push(JSON.stringify(obs));
11717
+ }
11718
+ }
11719
+ return sha256Hex(lines.join(`
11720
+ `));
11721
+ }
11722
+
11498
11723
  // src/storage/graph/soakRecorder.ts
11499
11724
  var SOAK_EVIDENCE_DIR = ".opencode/soak";
11500
11725
  var SOAK_RECORDER_BASE_DIR = ".opencode";
11501
11726
  var SOAK_RECORDER_ID_FILENAME = "soak-recorder-id";
11502
11727
  var SOAK_RECORDER_ID_PATH = `${SOAK_RECORDER_BASE_DIR}/${SOAK_RECORDER_ID_FILENAME}`;
11728
+ async function readAllChains(ports, evidenceDir) {
11729
+ if (!await ports.fs.exists(evidenceDir)) {
11730
+ return { chains: [], rejectedLines: 0, rejections: [] };
11731
+ }
11732
+ const files = await ports.fs.readDir(evidenceDir);
11733
+ const chains = [];
11734
+ const rejections = [];
11735
+ for (const file of files) {
11736
+ if (!file.endsWith(".jsonl")) {
11737
+ continue;
11738
+ }
11739
+ const content = await ports.fs.readFile(`${evidenceDir}/${file}`);
11740
+ const lines = content.split(`
11741
+ `);
11742
+ const observations = [];
11743
+ for (const [index, line] of lines.entries()) {
11744
+ if (line.trim().length === 0) {
11745
+ continue;
11746
+ }
11747
+ const lineNumber = index + 1;
11748
+ let rawParsed;
11749
+ try {
11750
+ rawParsed = JSON.parse(line);
11751
+ } catch {
11752
+ rejections.push({ file, lineNumber, detail: "invalid JSON" });
11753
+ continue;
11754
+ }
11755
+ const parsed = SoakObservationSchema.safeParse(rawParsed);
11756
+ if (parsed.success) {
11757
+ observations.push(parsed.data);
11758
+ } else {
11759
+ rejections.push({
11760
+ file,
11761
+ lineNumber,
11762
+ detail: formatSoakObservationIssues(parsed.error.issues)
11763
+ });
11764
+ }
11765
+ }
11766
+ if (observations.length > 0) {
11767
+ chains.push(observations);
11768
+ }
11769
+ }
11770
+ return { chains, rejectedLines: rejections.length, rejections };
11771
+ }
11772
+ function formatSoakObservationIssues(issues) {
11773
+ return issues.map((i) => `${i.path.join(".")}: ${i.code}`).join("; ");
11774
+ }
11503
11775
 
11504
11776
  // src/commands/frontierCatalog.ts
11505
11777
  function weightedCost(inputUSD, outputUSD) {
@@ -13272,8 +13544,7 @@ async function runAgents(deps, configPath, opencodeConfigPaths, configResolution
13272
13544
  async function runGraph(positionals, deps, configPath, configResolution) {
13273
13545
  const sub = positionals[1];
13274
13546
  if (sub === "status") {
13275
- const snapshot = await deps.readGraphSnapshot(configPath, configResolution);
13276
- return { exitCode: 0, stdout: renderGraphStatus(snapshot) };
13547
+ return runGraphStatus(deps, configPath, configResolution);
13277
13548
  }
13278
13549
  if (sub === "cancel") {
13279
13550
  return runGraphCancel(positionals, deps, configPath, configResolution);
@@ -13328,7 +13599,9 @@ function renderGraphOutcome(outcome) {
13328
13599
  async function resolveOperatorGraphPolicy(config, operator) {
13329
13600
  let graph = resolveGraphConfig(config, "file");
13330
13601
  if (isGateApplicable(graph.effectiveMode)) {
13331
- graph = applyGraphGate(graph, await operator.evaluateCutoverGate());
13602
+ const evaluation = await operator.evaluateCutoverGate(config);
13603
+ const gate = evaluation.kind === "evaluated" ? evaluation.gate : { allowed: false, violations: [], advisories: [] };
13604
+ graph = applyGraphGate(graph, gate);
13332
13605
  }
13333
13606
  return {
13334
13607
  graph,
@@ -13346,6 +13619,36 @@ function checkOperatorGate(policy) {
13346
13619
  stdout: renderLegacyGraphReason(decision2)
13347
13620
  };
13348
13621
  }
13622
+ async function runGraphStatus(deps, configPath, configResolution) {
13623
+ const snapshot = await deps.readGraphSnapshot(configPath, configResolution);
13624
+ if (!isGateApplicable(snapshot.effectiveMode)) {
13625
+ return { exitCode: 0, stdout: renderGraphStatus(snapshot) };
13626
+ }
13627
+ const operator = deps.graphOperator;
13628
+ if (operator === undefined) {
13629
+ const gate2 = {
13630
+ kind: "unevaluable",
13631
+ reason: "graph operator ports are not configured"
13632
+ };
13633
+ return { exitCode: 0, stdout: renderGraphStatus(snapshot, gate2) };
13634
+ }
13635
+ const config = await deps.loadConfig(configPath, configResolution);
13636
+ const evaluation = await operator.evaluateCutoverGate(config);
13637
+ if (evaluation.kind === "unevaluable") {
13638
+ const gate2 = {
13639
+ kind: "unevaluable",
13640
+ reason: evaluation.reason
13641
+ };
13642
+ return { exitCode: 0, stdout: renderGraphStatus(snapshot, gate2) };
13643
+ }
13644
+ const resolved = applyGraphGate(resolveGraphConfig(config, "file"), evaluation.gate);
13645
+ const gate = resolved.gateDenied === true ? {
13646
+ kind: "denied",
13647
+ violations: resolved.gateViolations ?? [],
13648
+ advisories: evaluation.gate.advisories
13649
+ } : { kind: "passed", advisories: evaluation.gate.advisories };
13650
+ return { exitCode: 0, stdout: renderGraphStatus(snapshot, gate) };
13651
+ }
13349
13652
  async function runGraphCancel(positionals, deps, configPath, configResolution) {
13350
13653
  const runID = positionals[2];
13351
13654
  if (runID === undefined || runID.trim() === "") {
@@ -13792,6 +14095,9 @@ async function runCli(argv, deps) {
13792
14095
  const summary = summarizeQueueWaits(queueEvents);
13793
14096
  queueWait = summary !== undefined ? { summary } : {};
13794
14097
  }
14098
+ const rosterMissing = deps.loadRosterForDoctor !== undefined && roster === undefined && rosterLoadError === undefined;
14099
+ const orchestratorAgentName = ORCHESTRATOR_AGENT_PATH.slice(ORCHESTRATOR_AGENT_PATH.lastIndexOf("/") + 1).replace(/\.md$/, "");
14100
+ const orchestratorAgentMissing = !agentFiles.some((file) => file.name === orchestratorAgentName);
13795
14101
  return {
13796
14102
  exitCode: 0,
13797
14103
  stdout: renderDoctor({
@@ -13805,6 +14111,8 @@ async function runCli(argv, deps) {
13805
14111
  ...rosterAudit !== undefined ? { rosterAudit } : {},
13806
14112
  ...rosterRoleCount !== undefined ? { rosterRoleCount } : {},
13807
14113
  ...rosterLoadError !== undefined ? { rosterLoadError } : {},
14114
+ ...rosterMissing ? { rosterMissing } : {},
14115
+ ...orchestratorAgentMissing ? { orchestratorAgentMissing } : {},
13808
14116
  ...diagnostics !== undefined && diagnostics.length > 0 ? { diagnostics } : {},
13809
14117
  ...opencodeConfigError !== undefined ? { opencodeConfigError } : {},
13810
14118
  ...cacheEntries !== undefined ? { cacheEntries, cliVersion: deps.version } : {},
@@ -14258,461 +14566,1031 @@ function planStartupHygieneMigration(exists) {
14258
14566
  return planLegacyMigration(exists).filter((move) => move.portable === false && move.relocation === "move" && move.from.startsWith(".opencode/openteam/"));
14259
14567
  }
14260
14568
 
14261
- // src/local/cacheAdapter.ts
14262
- import * as fs3 from "node:fs";
14263
- import * as os from "node:os";
14264
- import * as path3 from "node:path";
14265
- var SCOPE_PREFIX2 = "@jmanuelcorral/openteam";
14266
- var SCOPE_SEGMENTS = SCOPE_PREFIX2.split("/");
14267
- function resolveCacheRoot(override) {
14268
- if (override !== undefined && override.trim() !== "") {
14269
- return override;
14569
+ // src/index.ts
14570
+ import { readFile as readFile2 } from "node:fs/promises";
14571
+ import { basename as basename2, dirname as dirname5 } from "node:path";
14572
+ import {
14573
+ tool as tool2
14574
+ } from "@opencode-ai/plugin";
14575
+
14576
+ // src/graph/certificate.ts
14577
+ import { z as z20 } from "zod";
14578
+
14579
+ // src/contract/opencode.ts
14580
+ var SUPPORTED_OPENCODE_VERSIONS = [
14581
+ "1.17.13",
14582
+ "1.18.18",
14583
+ "1.18.19"
14584
+ ];
14585
+ var SEMVER_RE = /^(\d+)\.(\d+)\.(\d+)/;
14586
+ function parseSemver(version) {
14587
+ const normalized = version.trim().replace(/^[=^~v]+/, "");
14588
+ const match = SEMVER_RE.exec(normalized);
14589
+ if (match === null) {
14590
+ throw new Error(`invalid semver: "${version}"`);
14270
14591
  }
14271
- const base = process.env.XDG_CACHE_HOME ?? path3.join(os.homedir(), ".cache");
14272
- return path3.join(base, "opencode", "packages");
14592
+ return {
14593
+ major: Number(match[1]),
14594
+ minor: Number(match[2]),
14595
+ patch: Number(match[3])
14596
+ };
14273
14597
  }
14274
- function readJsonSafe(filePath) {
14275
- try {
14276
- const text2 = fs3.readFileSync(filePath, "utf8");
14277
- const parsed = JSON.parse(text2);
14278
- if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
14279
- return parsed;
14280
- }
14281
- return;
14282
- } catch {
14283
- return;
14598
+ function isCertificateVersionCompatible(certifiedVersion, liveVersion) {
14599
+ if (certifiedVersion === liveVersion) {
14600
+ return true;
14284
14601
  }
14285
- }
14286
- function extractVersion(pkg) {
14287
- if (pkg === undefined)
14288
- return "";
14289
- const v = pkg.version;
14290
- return typeof v === "string" ? v : "";
14291
- }
14292
- function readDirectoryEntries(dir) {
14602
+ let certified;
14603
+ let live;
14293
14604
  try {
14294
- return fs3.readdirSync(dir, { withFileTypes: true });
14605
+ certified = parseSemver(certifiedVersion);
14606
+ live = parseSemver(liveVersion);
14295
14607
  } catch {
14296
- return [];
14608
+ return false;
14609
+ }
14610
+ if (certified.major !== live.major || certified.minor !== live.minor) {
14611
+ return false;
14297
14612
  }
14613
+ return live.patch >= certified.patch;
14298
14614
  }
14299
- function lstatPlainDirectory(dir) {
14300
- try {
14301
- const stat2 = fs3.lstatSync(dir);
14302
- if (!stat2.isDirectory() || stat2.isSymbolicLink()) {
14303
- return;
14304
- }
14305
- return stat2;
14306
- } catch {
14307
- return;
14615
+
14616
+ // src/graph/certificate.ts
14617
+ var SHADOW_CERTIFICATE_MIN_RUNS = 1000;
14618
+ var DivergenceCodeSchema = z20.enum([
14619
+ "status",
14620
+ "order",
14621
+ "roles",
14622
+ "models",
14623
+ "fixes",
14624
+ "outcome"
14625
+ ]);
14626
+ var ProvenanceSchema = z20.enum([
14627
+ "recomputed",
14628
+ "compiled",
14629
+ "validated",
14630
+ "carried"
14631
+ ]);
14632
+ var EvidenceSchema = z20.object({
14633
+ opencodeVersion: z20.string().min(1),
14634
+ parity: z20.object({
14635
+ runs: z20.number().int().nonnegative(),
14636
+ match: z20.number().int().nonnegative(),
14637
+ divergent: z20.number().int().nonnegative(),
14638
+ inconclusive: z20.number().int().nonnegative(),
14639
+ divergences: z20.array(DivergenceCodeSchema)
14640
+ }).strict(),
14641
+ legacy: z20.object({
14642
+ observed: z20.number().int().nonnegative(),
14643
+ duplicated: z20.number().int().nonnegative()
14644
+ }).strict(),
14645
+ privacy: z20.object({
14646
+ surfacesScanned: z20.number().int().nonnegative(),
14647
+ rawFindings: z20.number().int().nonnegative()
14648
+ }).strict(),
14649
+ overhead: z20.object({
14650
+ p95Millis: z20.number().nonnegative(),
14651
+ budgetMillis: z20.number().positive(),
14652
+ samples: z20.number().int().nonnegative()
14653
+ }).strict(),
14654
+ provenance: z20.record(DivergenceCodeSchema, ProvenanceSchema)
14655
+ }).strict();
14656
+ var CertificateSchema = z20.object({
14657
+ version: z20.literal(1),
14658
+ certificate: z20.literal("graph-shadow"),
14659
+ opencodeVersion: z20.string().min(1),
14660
+ evidence: EvidenceSchema,
14661
+ verdict: z20.enum(["pass", "fail"]),
14662
+ failedGates: z20.array(z20.enum([
14663
+ "sample",
14664
+ "parity",
14665
+ "one-legacy-execution",
14666
+ "privacy",
14667
+ "overhead"
14668
+ ])),
14669
+ digest: z20.string().regex(/^[0-9a-f]{64}$/)
14670
+ }).strict();
14671
+
14672
+ class ShadowCertificateError extends Error {
14673
+ code;
14674
+ detail;
14675
+ constructor(code, detail) {
14676
+ super(`${code}: ${detail}`);
14677
+ this.name = "ShadowCertificateError";
14678
+ this.code = code;
14679
+ this.detail = detail;
14308
14680
  }
14309
14681
  }
14310
- function findEntryDirs(cacheRoot) {
14311
- const candidates = [];
14312
- for (const topEntry of readDirectoryEntries(cacheRoot)) {
14313
- const topPath = path3.join(cacheRoot, topEntry.name);
14314
- const topStat = lstatPlainDirectory(topPath);
14315
- if (topStat === undefined) {
14316
- continue;
14317
- }
14318
- if (!topEntry.name.startsWith("@")) {
14319
- candidates.push({
14320
- specDir: topEntry.name,
14321
- absolutePath: topPath,
14322
- stat: topStat
14323
- });
14324
- continue;
14325
- }
14326
- for (const packageEntry of readDirectoryEntries(topPath)) {
14327
- const packagePath = path3.join(topPath, packageEntry.name);
14328
- const packageStat = lstatPlainDirectory(packagePath);
14329
- if (packageStat === undefined) {
14330
- continue;
14331
- }
14332
- candidates.push({
14333
- specDir: `${topEntry.name}/${packageEntry.name}`,
14334
- absolutePath: packagePath,
14335
- stat: packageStat
14336
- });
14337
- }
14682
+ function evaluateGates(evidence) {
14683
+ const failed = [];
14684
+ if (evidence.parity.runs < SHADOW_CERTIFICATE_MIN_RUNS) {
14685
+ failed.push("sample");
14338
14686
  }
14339
- return candidates.sort((a, b) => a.specDir.localeCompare(b.specDir));
14687
+ if (evidence.parity.divergent > 0 || evidence.parity.inconclusive > 0 || evidence.parity.divergences.length > 0 || evidence.parity.match !== evidence.parity.runs) {
14688
+ failed.push("parity");
14689
+ }
14690
+ if (evidence.legacy.duplicated > 0 || evidence.legacy.observed < 1) {
14691
+ failed.push("one-legacy-execution");
14692
+ }
14693
+ if (evidence.privacy.rawFindings > 0 || evidence.privacy.surfacesScanned < 1) {
14694
+ failed.push("privacy");
14695
+ }
14696
+ if (evidence.overhead.samples < 1 || evidence.overhead.p95Millis > evidence.overhead.budgetMillis) {
14697
+ failed.push("overhead");
14698
+ }
14699
+ return failed;
14340
14700
  }
14341
- async function listEntries(cacheRoot) {
14342
- const entries = [];
14343
- for (const candidate of findEntryDirs(cacheRoot)) {
14344
- const { specDir, absolutePath, stat: stat2 } = candidate;
14345
- if (!specDir.startsWith(SCOPE_PREFIX2))
14346
- continue;
14347
- const mtimeIso = stat2.mtime.toISOString();
14348
- const entryPkg = readJsonSafe(path3.join(absolutePath, "package.json"));
14349
- const deps = entryPkg?.dependencies;
14350
- let pinnedVersion = "";
14351
- if (deps !== null && typeof deps === "object" && !Array.isArray(deps)) {
14352
- const depsObj = deps;
14353
- const pinned = depsObj[SCOPE_PREFIX2];
14354
- pinnedVersion = typeof pinned === "string" ? pinned : "";
14701
+ function shadowCertificateBindingDigest(evidence) {
14702
+ return sha256Hex(JSON.stringify({
14703
+ opencodeVersion: evidence.opencodeVersion,
14704
+ parity: {
14705
+ runs: evidence.parity.runs,
14706
+ match: evidence.parity.match,
14707
+ divergent: evidence.parity.divergent,
14708
+ inconclusive: evidence.parity.inconclusive,
14709
+ divergences: [...evidence.parity.divergences].sort()
14710
+ },
14711
+ legacy: {
14712
+ observed: evidence.legacy.observed,
14713
+ duplicated: evidence.legacy.duplicated
14714
+ },
14715
+ privacy: {
14716
+ surfacesScanned: evidence.privacy.surfacesScanned,
14717
+ rawFindings: evidence.privacy.rawFindings
14718
+ },
14719
+ overhead: {
14720
+ budgetMillis: evidence.overhead.budgetMillis,
14721
+ samples: evidence.overhead.samples
14722
+ },
14723
+ provenance: Object.fromEntries(Object.entries(evidence.provenance).sort(([a], [b]) => a < b ? -1 : 1))
14724
+ }));
14725
+ }
14726
+ function canonicalCertificate(certificate) {
14727
+ const evidence = certificate.evidence;
14728
+ return JSON.stringify({
14729
+ version: certificate.version,
14730
+ certificate: certificate.certificate,
14731
+ opencodeVersion: certificate.opencodeVersion,
14732
+ verdict: certificate.verdict,
14733
+ failedGates: [...certificate.failedGates],
14734
+ evidence: {
14735
+ opencodeVersion: evidence.opencodeVersion,
14736
+ parity: {
14737
+ runs: evidence.parity.runs,
14738
+ match: evidence.parity.match,
14739
+ divergent: evidence.parity.divergent,
14740
+ inconclusive: evidence.parity.inconclusive,
14741
+ divergences: [...evidence.parity.divergences].sort()
14742
+ },
14743
+ legacy: {
14744
+ observed: evidence.legacy.observed,
14745
+ duplicated: evidence.legacy.duplicated
14746
+ },
14747
+ privacy: {
14748
+ surfacesScanned: evidence.privacy.surfacesScanned,
14749
+ rawFindings: evidence.privacy.rawFindings
14750
+ },
14751
+ overhead: {
14752
+ p95Millis: evidence.overhead.p95Millis,
14753
+ budgetMillis: evidence.overhead.budgetMillis,
14754
+ samples: evidence.overhead.samples
14755
+ },
14756
+ provenance: Object.fromEntries(Object.entries(evidence.provenance).sort(([a], [b]) => a < b ? -1 : 1))
14355
14757
  }
14356
- const installedPkg = readJsonSafe(path3.join(absolutePath, "node_modules", ...SCOPE_SEGMENTS, "package.json"));
14357
- const installedVersion = extractVersion(installedPkg);
14358
- entries.push({
14359
- specDir,
14360
- absolutePath,
14361
- pinnedVersion,
14362
- installedVersion,
14363
- mtimeIso
14364
- });
14365
- }
14366
- return entries;
14758
+ });
14367
14759
  }
14368
- async function deleteEntry(entryPath) {
14369
- fs3.rmSync(entryPath, { recursive: true, force: true });
14760
+ function sameGates(a, b) {
14761
+ return a.length === b.length && a.every((gate, index) => gate === b[index]);
14370
14762
  }
14371
- async function isReparsePath(entryPath) {
14372
- try {
14373
- const stat2 = await fs3.promises.lstat(entryPath);
14374
- return stat2.isSymbolicLink();
14375
- } catch {
14376
- return true;
14763
+ function parseShadowCertificate(value, opencodeVersion) {
14764
+ const parsed = CertificateSchema.safeParse(value);
14765
+ if (!parsed.success) {
14766
+ const detail = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.code}`).join("; ");
14767
+ throw new ShadowCertificateError("invalid-certificate", detail);
14768
+ }
14769
+ const certificate = parsed.data;
14770
+ const failedGates = evaluateGates(certificate.evidence);
14771
+ const verdict = failedGates.length === 0 ? "pass" : "fail";
14772
+ if (certificate.verdict !== verdict || !sameGates(certificate.failedGates, failedGates)) {
14773
+ throw new ShadowCertificateError("verdict-mismatch", `evidence yields "${verdict}" (${failedGates.join(",") || "no failed gate"})`);
14774
+ }
14775
+ const { digest, ...unsigned } = certificate;
14776
+ if (sha256Hex(canonicalCertificate(unsigned)) !== digest) {
14777
+ throw new ShadowCertificateError("digest-mismatch", "certificate digest does not cover the presented evidence");
14377
14778
  }
14779
+ if (!isCertificateVersionCompatible(certificate.opencodeVersion, opencodeVersion)) {
14780
+ throw new ShadowCertificateError("opencode-mismatch", `certified for "${certificate.opencodeVersion}", running "${opencodeVersion}" — evidence is accepted only on ${certificate.opencodeVersion} or a newer patch of the same minor line`);
14781
+ }
14782
+ return certificate;
14378
14783
  }
14379
- var realCacheAdapter = {
14380
- resolveCacheRoot,
14381
- listEntries,
14382
- deleteEntry,
14383
- isReparsePath
14384
- };
14784
+ var ReleaseDeterminismSchema = z20.object({ runs: z20.number().int().nonnegative(), allMatch: z20.boolean() }).strict();
14785
+ var ReleaseCrashRecoverySchema = z20.object({
14786
+ scenarios: z20.number().int().nonnegative(),
14787
+ allConverge: z20.boolean()
14788
+ }).strict();
14789
+ var ReleaseReplayEquivalenceSchema = z20.object({
14790
+ checks: z20.number().int().nonnegative(),
14791
+ allEquivalent: z20.boolean()
14792
+ }).strict();
14793
+ var ReleasePrivacySchema = z20.object({
14794
+ surfacesScanned: z20.number().int().nonnegative(),
14795
+ rawFindings: z20.number().int().nonnegative()
14796
+ }).strict();
14797
+ var ReleaseContractSchema = z20.object({
14798
+ roundTrips: z20.number().int().nonnegative(),
14799
+ allSettled: z20.boolean()
14800
+ }).strict();
14801
+ var ReleaseHealthSchema = z20.object({
14802
+ samples: z20.number().int().nonnegative(),
14803
+ bounded: z20.boolean(),
14804
+ idempotent: z20.boolean()
14805
+ }).strict();
14806
+ var ReleasePerformanceSchema = z20.object({
14807
+ p95Millis: z20.number().nonnegative(),
14808
+ budgetMillis: z20.number().positive(),
14809
+ samples: z20.number().int().nonnegative()
14810
+ }).strict();
14811
+ var ReleaseEvidenceSchema = z20.object({
14812
+ opencodeVersion: z20.string().min(1),
14813
+ platform: z20.enum(["linux", "win32"]),
14814
+ shadowCertificateDigest: z20.string().regex(/^[0-9a-f]{64}$/),
14815
+ determinism: ReleaseDeterminismSchema,
14816
+ crashRecovery: ReleaseCrashRecoverySchema,
14817
+ replayEquivalence: ReleaseReplayEquivalenceSchema,
14818
+ privacy: ReleasePrivacySchema,
14819
+ contract: ReleaseContractSchema,
14820
+ health: ReleaseHealthSchema,
14821
+ performance: ReleasePerformanceSchema
14822
+ }).strict();
14823
+ var ReleaseCertificateSchema = z20.object({
14824
+ version: z20.literal(2),
14825
+ certificate: z20.literal("graph-release"),
14826
+ platform: z20.enum(["linux", "win32"]),
14827
+ opencodeVersion: z20.string().min(1),
14828
+ shadowCertificateDigest: z20.string().regex(/^[0-9a-f]{64}$/),
14829
+ evidence: ReleaseEvidenceSchema,
14830
+ verdict: z20.enum(["pass", "fail"]),
14831
+ failedGates: z20.array(z20.enum([
14832
+ "shadow-valid",
14833
+ "determinism",
14834
+ "crash-recovery",
14835
+ "replay-equivalence",
14836
+ "privacy",
14837
+ "contract",
14838
+ "health",
14839
+ "performance"
14840
+ ])),
14841
+ digest: z20.string().regex(/^[0-9a-f]{64}$/)
14842
+ }).strict();
14385
14843
 
14386
- // src/memory/rank.ts
14387
- var DAY_MS = 86400000;
14388
- var DEFAULT_RANK_SIGNALS = {
14389
- similarityWeight: 1,
14390
- recencyWeight: 0.2,
14391
- accessWeight: 0.1,
14392
- recencyHalfLifeMs: 14 * DAY_MS
14393
- };
14394
- function recencyTimestamp(record) {
14395
- if (record.kind === "preference" && record.lastAccessedAt !== null) {
14396
- return record.lastAccessedAt;
14844
+ class ReleaseCertificateError extends Error {
14845
+ code;
14846
+ detail;
14847
+ constructor(code, detail) {
14848
+ super(`${code}: ${detail}`);
14849
+ this.name = "ReleaseCertificateError";
14850
+ this.code = code;
14851
+ this.detail = detail;
14397
14852
  }
14398
- return record.createdAt;
14399
- }
14400
- function recencyScore(record, signals) {
14401
- const ageMs = Math.max(0, signals.now - recencyTimestamp(record));
14402
- return 0.5 ** (ageMs / signals.recencyHalfLifeMs);
14403
14853
  }
14404
- function accessScore(record) {
14405
- if (record.kind !== "preference") {
14406
- return 0;
14854
+ function evaluateReleaseGates(evidence, expectedShadowDigest) {
14855
+ const failed = [];
14856
+ if (evidence.shadowCertificateDigest !== expectedShadowDigest) {
14857
+ failed.push("shadow-valid");
14407
14858
  }
14408
- return 1 - 1 / (1 + record.accessCount);
14409
- }
14410
- function scoreCandidate(candidate, signals) {
14411
- return signals.similarityWeight * candidate.similarity + signals.recencyWeight * recencyScore(candidate.record, signals) + signals.accessWeight * accessScore(candidate.record);
14859
+ if (evidence.determinism.runs < 1 || !evidence.determinism.allMatch) {
14860
+ failed.push("determinism");
14861
+ }
14862
+ if (evidence.crashRecovery.scenarios < 1 || !evidence.crashRecovery.allConverge) {
14863
+ failed.push("crash-recovery");
14864
+ }
14865
+ if (evidence.replayEquivalence.checks < 1 || !evidence.replayEquivalence.allEquivalent) {
14866
+ failed.push("replay-equivalence");
14867
+ }
14868
+ if (evidence.privacy.surfacesScanned < 1 || evidence.privacy.rawFindings > 0) {
14869
+ failed.push("privacy");
14870
+ }
14871
+ if (evidence.contract.roundTrips < 1 || !evidence.contract.allSettled) {
14872
+ failed.push("contract");
14873
+ }
14874
+ if (evidence.health.samples < 1 || !evidence.health.bounded || !evidence.health.idempotent) {
14875
+ failed.push("health");
14876
+ }
14877
+ if (evidence.performance.samples < 1 || evidence.performance.p95Millis > evidence.performance.budgetMillis) {
14878
+ failed.push("performance");
14879
+ }
14880
+ return failed;
14412
14881
  }
14413
- function rankRecall(candidates, signals) {
14414
- return candidates.map((candidate) => ({
14415
- ...candidate,
14416
- score: scoreCandidate(candidate, signals)
14417
- })).sort((a, b) => {
14418
- if (b.score !== a.score) {
14419
- return b.score - a.score;
14882
+ function canonicalReleaseCertificate(certificate) {
14883
+ const ev = certificate.evidence;
14884
+ return JSON.stringify({
14885
+ version: certificate.version,
14886
+ certificate: certificate.certificate,
14887
+ platform: certificate.platform,
14888
+ opencodeVersion: certificate.opencodeVersion,
14889
+ shadowCertificateDigest: certificate.shadowCertificateDigest,
14890
+ verdict: certificate.verdict,
14891
+ failedGates: [...certificate.failedGates],
14892
+ evidence: {
14893
+ opencodeVersion: ev.opencodeVersion,
14894
+ platform: ev.platform,
14895
+ shadowCertificateDigest: ev.shadowCertificateDigest,
14896
+ determinism: {
14897
+ runs: ev.determinism.runs,
14898
+ allMatch: ev.determinism.allMatch
14899
+ },
14900
+ crashRecovery: {
14901
+ scenarios: ev.crashRecovery.scenarios,
14902
+ allConverge: ev.crashRecovery.allConverge
14903
+ },
14904
+ replayEquivalence: {
14905
+ checks: ev.replayEquivalence.checks,
14906
+ allEquivalent: ev.replayEquivalence.allEquivalent
14907
+ },
14908
+ privacy: {
14909
+ surfacesScanned: ev.privacy.surfacesScanned,
14910
+ rawFindings: ev.privacy.rawFindings
14911
+ },
14912
+ contract: {
14913
+ roundTrips: ev.contract.roundTrips,
14914
+ allSettled: ev.contract.allSettled
14915
+ },
14916
+ health: {
14917
+ samples: ev.health.samples,
14918
+ bounded: ev.health.bounded,
14919
+ idempotent: ev.health.idempotent
14920
+ },
14921
+ performance: {
14922
+ p95Millis: ev.performance.p95Millis,
14923
+ budgetMillis: ev.performance.budgetMillis,
14924
+ samples: ev.performance.samples
14925
+ }
14420
14926
  }
14421
- return a.record.id < b.record.id ? -1 : a.record.id > b.record.id ? 1 : 0;
14422
14927
  });
14423
14928
  }
14929
+ function sameReleaseGates(a, b) {
14930
+ return a.length === b.length && a.every((gate, index) => gate === b[index]);
14931
+ }
14932
+ function parseReleaseCertificate(value, opencodeVersion, expectedShadowDigest) {
14933
+ if (typeof value === "object" && value !== null && "version" in value && value.version === 1) {
14934
+ throw new ReleaseCertificateError("invalid-certificate", "certificate is schema version 1 (carried a removed 'migration' gate); " + "rebuild the release certificate against the current schema");
14935
+ }
14936
+ const parsed = ReleaseCertificateSchema.safeParse(value);
14937
+ if (!parsed.success) {
14938
+ const detail = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.code}`).join("; ");
14939
+ throw new ReleaseCertificateError("invalid-certificate", detail);
14940
+ }
14941
+ const certificate = parsed.data;
14942
+ const inconsistencies = [];
14943
+ if (certificate.platform !== certificate.evidence.platform) {
14944
+ inconsistencies.push("platform");
14945
+ }
14946
+ if (certificate.opencodeVersion !== certificate.evidence.opencodeVersion) {
14947
+ inconsistencies.push("opencodeVersion");
14948
+ }
14949
+ if (certificate.shadowCertificateDigest !== certificate.evidence.shadowCertificateDigest) {
14950
+ inconsistencies.push("shadowCertificateDigest");
14951
+ }
14952
+ if (inconsistencies.length > 0) {
14953
+ throw new ReleaseCertificateError("field-inconsistency", `top-level/evidence divergence: ${inconsistencies.join(", ")}`);
14954
+ }
14955
+ const failedGates = evaluateReleaseGates(certificate.evidence, expectedShadowDigest);
14956
+ const verdict = failedGates.length === 0 ? "pass" : "fail";
14957
+ if (certificate.verdict !== verdict || !sameReleaseGates(certificate.failedGates, failedGates)) {
14958
+ throw new ReleaseCertificateError("verdict-mismatch", `evidence yields "${verdict}" (${failedGates.join(",") || "no failed gate"})`);
14959
+ }
14960
+ const { digest, ...unsigned } = certificate;
14961
+ if (sha256Hex(canonicalReleaseCertificate(unsigned)) !== digest) {
14962
+ throw new ReleaseCertificateError("digest-mismatch", "certificate digest does not cover the presented evidence");
14963
+ }
14964
+ if (!isCertificateVersionCompatible(certificate.opencodeVersion, opencodeVersion)) {
14965
+ throw new ReleaseCertificateError("opencode-mismatch", `certified for "${certificate.opencodeVersion}", running "${opencodeVersion}" — evidence is accepted only on ${certificate.opencodeVersion} or a newer patch of the same minor line`);
14966
+ }
14967
+ return certificate;
14968
+ }
14969
+ var ShadowDivergenceCodeSchema = z20.enum([
14970
+ "status",
14971
+ "order",
14972
+ "roles",
14973
+ "models",
14974
+ "fixes",
14975
+ "outcome"
14976
+ ]);
14977
+ var RECOMPUTED_DIVERGENCE_CODES = ["fixes"];
14978
+ var RecomputedDivergenceCodeSchema = z20.enum(RECOMPUTED_DIVERGENCE_CODES);
14979
+ var SoakPolicySchema = z20.object({
14980
+ minimumGenuineObservations: z20.number().int().positive(),
14981
+ minimumDistinctDays: z20.number().int().positive(),
14982
+ requireGenuineUsageOnAllPlatforms: z20.boolean(),
14983
+ supportedOpencodeVersions: z20.array(z20.string().min(1)).min(1),
14984
+ criticalDivergenceCodes: z20.array(RecomputedDivergenceCodeSchema).min(1)
14985
+ }).strict();
14986
+ var DEFAULT_SOAK_POLICY = {
14987
+ minimumGenuineObservations: 100,
14988
+ minimumDistinctDays: 14,
14989
+ requireGenuineUsageOnAllPlatforms: true,
14990
+ supportedOpencodeVersions: [...SUPPORTED_OPENCODE_VERSIONS],
14991
+ criticalDivergenceCodes: ["fixes"]
14992
+ };
14993
+ var SoakPlatformEnumSchema = z20.enum(["linux", "win32", "darwin"]);
14994
+ var SoakPrivacyPairEvidenceSchema = z20.object({
14995
+ surfacesScanned: z20.number().int().nonnegative(),
14996
+ rawFindings: z20.number().int().nonnegative()
14997
+ }).strict();
14998
+ var SoakDuplicateEffectPairEvidenceSchema = z20.object({
14999
+ effectsExamined: z20.number().int().nonnegative(),
15000
+ duplicatesFound: z20.number().int().nonnegative()
15001
+ }).strict();
15002
+ var SoakModelVerificationPairEvidenceSchema = z20.object({
15003
+ nodesChecked: z20.number().int().nonnegative(),
15004
+ unverified: z20.number().int().nonnegative(),
15005
+ mismatched: z20.number().int().nonnegative()
15006
+ }).strict();
15007
+ var SoakEvidenceSchema = z20.object({
15008
+ totalObservations: z20.number().int().nonnegative(),
15009
+ genuineUsageObservations: z20.number().int().nonnegative(),
15010
+ ciSyntheticObservations: z20.number().int().nonnegative(),
15011
+ recorderCount: z20.number().int().nonnegative(),
15012
+ ineligibleObservations: z20.number().int().nonnegative(),
15013
+ firstTimestamp: z20.string().datetime().nullable(),
15014
+ lastTimestamp: z20.string().datetime().nullable(),
15015
+ distinctDays: z20.number().int().nonnegative(),
15016
+ criticalDivergences: z20.number().int().nonnegative().nullable(),
15017
+ privacy: SoakPrivacyPairEvidenceSchema.nullable(),
15018
+ duplicateEffects: SoakDuplicateEffectPairEvidenceSchema.nullable(),
15019
+ modelVerification: SoakModelVerificationPairEvidenceSchema.nullable(),
15020
+ platformsGenuine: z20.array(SoakPlatformEnumSchema),
15021
+ platformsSynthetic: z20.array(SoakPlatformEnumSchema),
15022
+ opencodeVersionsObserved: z20.array(z20.string().min(1)),
15023
+ invalidChains: z20.number().int().nonnegative()
15024
+ }).strict();
15025
+ var SoakCertificateSchema = z20.object({
15026
+ version: z20.literal(1),
15027
+ certificate: z20.literal("graph-soak"),
15028
+ trustBoundary: z20.literal("contributors"),
15029
+ policy: SoakPolicySchema,
15030
+ evidence: SoakEvidenceSchema,
15031
+ verdict: z20.enum(["pass", "fail"]),
15032
+ failedGates: z20.array(z20.enum([
15033
+ "minimum-observations",
15034
+ "minimum-duration",
15035
+ "chain-integrity",
15036
+ "zero-critical-divergence",
15037
+ "zero-privacy-breach",
15038
+ "zero-duplicate-effect",
15039
+ "zero-model-mismatch",
15040
+ "os-diversity",
15041
+ "opencode-pin"
15042
+ ])),
15043
+ ledgerDigest: z20.string().regex(/^[0-9a-f]{64}$/),
15044
+ digest: z20.string().regex(/^[0-9a-f]{64}$/)
15045
+ }).strict();
14424
15046
 
14425
- // src/memory/temporal.ts
14426
- function isTransactionActiveAt(record, asOf) {
14427
- return record.invalidatedAt === null || record.invalidatedAt > asOf;
15047
+ class SoakCertificateError extends Error {
15048
+ code;
15049
+ detail;
15050
+ constructor(code, detail) {
15051
+ super(`${code}: ${detail}`);
15052
+ this.name = "SoakCertificateError";
15053
+ this.code = code;
15054
+ this.detail = detail;
15055
+ }
14428
15056
  }
14429
- function isValidAt(record, asOf) {
14430
- if (record.validFrom > asOf) {
14431
- return false;
15057
+ function evaluateSoakGates(evidence, policy) {
15058
+ const failed = [];
15059
+ if (evidence.genuineUsageObservations < policy.minimumGenuineObservations) {
15060
+ failed.push("minimum-observations");
14432
15061
  }
14433
- return record.validUntil === null || record.validUntil > asOf;
15062
+ if (evidence.distinctDays < policy.minimumDistinctDays) {
15063
+ failed.push("minimum-duration");
15064
+ }
15065
+ if (evidence.invalidChains > 0) {
15066
+ failed.push("chain-integrity");
15067
+ }
15068
+ if (evidence.criticalDivergences === null || evidence.criticalDivergences > 0) {
15069
+ failed.push("zero-critical-divergence");
15070
+ }
15071
+ if (evidence.privacy === null || evidence.privacy.surfacesScanned < 1 || evidence.privacy.rawFindings > 0) {
15072
+ failed.push("zero-privacy-breach");
15073
+ }
15074
+ if (evidence.duplicateEffects === null || evidence.duplicateEffects.effectsExamined < 1 || evidence.duplicateEffects.duplicatesFound > 0) {
15075
+ failed.push("zero-duplicate-effect");
15076
+ }
15077
+ if (evidence.modelVerification === null || evidence.modelVerification.nodesChecked < 1 || evidence.modelVerification.mismatched > 0) {
15078
+ failed.push("zero-model-mismatch");
15079
+ }
15080
+ if (policy.requireGenuineUsageOnAllPlatforms) {
15081
+ if (!evidence.platformsGenuine.includes("linux") || !evidence.platformsGenuine.includes("win32")) {
15082
+ failed.push("os-diversity");
15083
+ }
15084
+ } else {
15085
+ const allPlatforms = new Set([
15086
+ ...evidence.platformsGenuine,
15087
+ ...evidence.platformsSynthetic
15088
+ ]);
15089
+ if (!allPlatforms.has("linux") || !allPlatforms.has("win32") || evidence.platformsGenuine.length < 1) {
15090
+ failed.push("os-diversity");
15091
+ }
15092
+ }
15093
+ const supported = new Set(policy.supportedOpencodeVersions);
15094
+ if (evidence.opencodeVersionsObserved.length === 0 || !evidence.opencodeVersionsObserved.every((v) => supported.has(v))) {
15095
+ failed.push("opencode-pin");
15096
+ }
15097
+ return failed;
14434
15098
  }
14435
- function isActiveAt(record, asOf) {
14436
- return isTransactionActiveAt(record, asOf) && isValidAt(record, asOf);
15099
+ function canonicalSoakCertificate(certificate) {
15100
+ const ev = certificate.evidence;
15101
+ const pol = certificate.policy;
15102
+ return JSON.stringify({
15103
+ version: certificate.version,
15104
+ certificate: certificate.certificate,
15105
+ trustBoundary: certificate.trustBoundary,
15106
+ policy: {
15107
+ minimumGenuineObservations: pol.minimumGenuineObservations,
15108
+ minimumDistinctDays: pol.minimumDistinctDays,
15109
+ requireGenuineUsageOnAllPlatforms: pol.requireGenuineUsageOnAllPlatforms,
15110
+ supportedOpencodeVersions: [...pol.supportedOpencodeVersions].sort(),
15111
+ criticalDivergenceCodes: [...pol.criticalDivergenceCodes].sort()
15112
+ },
15113
+ verdict: certificate.verdict,
15114
+ failedGates: [...certificate.failedGates],
15115
+ ledgerDigest: certificate.ledgerDigest,
15116
+ evidence: {
15117
+ totalObservations: ev.totalObservations,
15118
+ genuineUsageObservations: ev.genuineUsageObservations,
15119
+ ciSyntheticObservations: ev.ciSyntheticObservations,
15120
+ recorderCount: ev.recorderCount,
15121
+ ineligibleObservations: ev.ineligibleObservations,
15122
+ firstTimestamp: ev.firstTimestamp,
15123
+ lastTimestamp: ev.lastTimestamp,
15124
+ distinctDays: ev.distinctDays,
15125
+ criticalDivergences: ev.criticalDivergences,
15126
+ privacy: ev.privacy,
15127
+ duplicateEffects: ev.duplicateEffects,
15128
+ modelVerification: ev.modelVerification,
15129
+ platformsGenuine: [...ev.platformsGenuine].sort(),
15130
+ platformsSynthetic: [...ev.platformsSynthetic].sort(),
15131
+ opencodeVersionsObserved: [...ev.opencodeVersionsObserved].sort(),
15132
+ invalidChains: ev.invalidChains
15133
+ }
15134
+ });
14437
15135
  }
14438
- function filterActive(records, asOf) {
14439
- return records.filter((record) => isActiveAt(record, asOf));
15136
+ function sameSoakGates(a, b) {
15137
+ return a.length === b.length && a.every((gate, index) => gate === b[index]);
14440
15138
  }
14441
- function coalesceWindow(a, b) {
14442
- const validFrom = Math.min(a.validFrom, b.validFrom);
14443
- const validUntil = a.validUntil === null || b.validUntil === null ? null : Math.max(a.validUntil, b.validUntil);
14444
- return { validFrom, validUntil };
15139
+ function sameSoakPolicy(a, b) {
15140
+ const aVersions = [...a.supportedOpencodeVersions].sort();
15141
+ const bVersions = [...b.supportedOpencodeVersions].sort();
15142
+ return a.minimumGenuineObservations === b.minimumGenuineObservations && a.minimumDistinctDays === b.minimumDistinctDays && a.requireGenuineUsageOnAllPlatforms === b.requireGenuineUsageOnAllPlatforms && aVersions.length === bVersions.length && aVersions.every((v, i) => v === bVersions[i]) && a.criticalDivergenceCodes.length === b.criticalDivergenceCodes.length && [...a.criticalDivergenceCodes].sort().every((code, i) => code === [...b.criticalDivergenceCodes].sort()[i]);
14445
15143
  }
14446
-
14447
- // src/memory/types.ts
14448
- import { z as z19 } from "zod";
14449
- var SHARED_OWNER_KEY = "*";
14450
- var OwnerKeySchema = z19.string().min(1);
14451
- var MemoryKindSchema = z19.enum(["fact", "preference", "entity"]);
14452
- var MemoryBaseSchema = z19.object({
14453
- id: z19.string().min(1),
14454
- ownerKey: OwnerKeySchema,
14455
- confidence: z19.number().min(0).max(1),
14456
- validFrom: z19.number().finite(),
14457
- validUntil: z19.number().finite().nullable().default(null),
14458
- createdAt: z19.number().finite(),
14459
- invalidatedAt: z19.number().finite().nullable().default(null),
14460
- sourceHash: z19.string().min(1),
14461
- supersededBy: z19.string().min(1).nullable().default(null)
14462
- }).strict();
14463
- var FactSchema = MemoryBaseSchema.extend({
14464
- kind: z19.literal("fact"),
14465
- subject: z19.string().min(1),
14466
- predicate: z19.string().min(1),
14467
- object: z19.string().min(1),
14468
- category: z19.string().min(1).nullable().default(null)
14469
- });
14470
- var PreferenceSchema = MemoryBaseSchema.extend({
14471
- kind: z19.literal("preference"),
14472
- category: z19.string().min(1),
14473
- preference: z19.string().min(1),
14474
- context: z19.string().min(1).nullable().default(null),
14475
- lastAccessedAt: z19.number().finite().nullable().default(null),
14476
- accessCount: z19.number().int().min(0).default(0)
14477
- });
14478
- var EntitySchema = MemoryBaseSchema.extend({
14479
- kind: z19.literal("entity"),
14480
- canonicalName: z19.string().min(1),
14481
- type: z19.string().min(1),
14482
- aliases: z19.array(z19.string().min(1)).default([])
14483
- });
14484
- var RelationSchema = MemoryBaseSchema.extend({
14485
- kind: z19.literal("relation"),
14486
- from: z19.string().min(1),
14487
- to: z19.string().min(1),
14488
- predicate: z19.string().min(1),
14489
- annotation: z19.string().min(1).nullable().default(null)
14490
- });
14491
- var MemoryRecordSchema = z19.discriminatedUnion("kind", [
14492
- FactSchema,
14493
- PreferenceSchema,
14494
- EntitySchema,
14495
- RelationSchema
14496
- ]);
14497
- var RecallQuerySchema = z19.object({
14498
- ownerKey: OwnerKeySchema,
14499
- kinds: z19.array(MemoryKindSchema).default(["fact", "preference", "entity"]),
14500
- asOf: z19.number().finite().nullable().default(null),
14501
- limit: z19.number().int().positive().default(8),
14502
- minSimilarity: z19.number().min(0).max(1).default(0.2),
14503
- includeShared: z19.boolean().default(true)
14504
- }).strict();
14505
-
14506
- // src/memory/vector.ts
14507
- function dot(a, b) {
14508
- if (a.length !== b.length) {
14509
- throw new Error(`dimension mismatch: ${a.length} vs ${b.length}`);
15144
+ function parseSoakCertificate(value, expectedPolicy, expectedLedgerDigest) {
15145
+ const parsed = SoakCertificateSchema.safeParse(value);
15146
+ if (!parsed.success) {
15147
+ const detail = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.code}`).join("; ");
15148
+ throw new SoakCertificateError("invalid-certificate", detail);
14510
15149
  }
14511
- let sum = 0;
14512
- for (let i = 0;i < a.length; i += 1) {
14513
- sum += a[i] * b[i];
15150
+ const certificate = parsed.data;
15151
+ if (!sameSoakPolicy(certificate.policy, expectedPolicy)) {
15152
+ throw new SoakCertificateError("policy-mismatch", "embedded policy diverges from the expected policy");
14514
15153
  }
14515
- return sum;
15154
+ const failedGates = evaluateSoakGates(certificate.evidence, certificate.policy);
15155
+ const verdict = failedGates.length === 0 ? "pass" : "fail";
15156
+ if (certificate.verdict !== verdict || !sameSoakGates(certificate.failedGates, failedGates)) {
15157
+ throw new SoakCertificateError("verdict-mismatch", `evidence yields "${verdict}" (${failedGates.join(",") || "no failed gate"})`);
15158
+ }
15159
+ const { digest, ...unsigned } = certificate;
15160
+ if (sha256Hex(canonicalSoakCertificate(unsigned)) !== digest) {
15161
+ throw new SoakCertificateError("digest-mismatch", "certificate digest does not cover the presented evidence");
15162
+ }
15163
+ if (certificate.ledgerDigest !== expectedLedgerDigest) {
15164
+ throw new SoakCertificateError("ledger-mismatch", "ledger digest does not match the evidence the caller holds");
15165
+ }
15166
+ return certificate;
14516
15167
  }
14517
- function norm(a) {
14518
- let sum = 0;
14519
- for (let i = 0;i < a.length; i += 1) {
14520
- const value = a[i];
14521
- sum += value * value;
15168
+
15169
+ // src/local/cacheAdapter.ts
15170
+ import * as fs3 from "node:fs";
15171
+ import * as os from "node:os";
15172
+ import * as path3 from "node:path";
15173
+ var SCOPE_PREFIX2 = "@jmanuelcorral/openteam";
15174
+ var SCOPE_SEGMENTS = SCOPE_PREFIX2.split("/");
15175
+ function resolveCacheRoot(override) {
15176
+ if (override !== undefined && override.trim() !== "") {
15177
+ return override;
14522
15178
  }
14523
- return Math.sqrt(sum);
15179
+ const base = process.env.XDG_CACHE_HOME ?? path3.join(os.homedir(), ".cache");
15180
+ return path3.join(base, "opencode", "packages");
14524
15181
  }
14525
- function cosineSimilarity(a, b) {
14526
- const denom = norm(a) * norm(b);
14527
- if (denom === 0) {
14528
- return 0;
15182
+ function readJsonSafe(filePath) {
15183
+ try {
15184
+ const text2 = fs3.readFileSync(filePath, "utf8");
15185
+ const parsed = JSON.parse(text2);
15186
+ if (parsed !== null && typeof parsed === "object" && !Array.isArray(parsed)) {
15187
+ return parsed;
15188
+ }
15189
+ return;
15190
+ } catch {
15191
+ return;
14529
15192
  }
14530
- return dot(a, b) / denom;
14531
15193
  }
14532
-
14533
- // src/memory/recall.ts
14534
- function matchesScope(record, query) {
14535
- if (record.ownerKey === query.ownerKey) {
14536
- return true;
15194
+ function extractVersion(pkg) {
15195
+ if (pkg === undefined)
15196
+ return "";
15197
+ const v = pkg.version;
15198
+ return typeof v === "string" ? v : "";
15199
+ }
15200
+ function readDirectoryEntries(dir) {
15201
+ try {
15202
+ return fs3.readdirSync(dir, { withFileTypes: true });
15203
+ } catch {
15204
+ return [];
14537
15205
  }
14538
- return query.includeShared && record.ownerKey === SHARED_OWNER_KEY;
14539
15206
  }
14540
- function recall(records, queryEmbedding, query, deps) {
14541
- const asOf = query.asOf ?? deps.now;
14542
- const kinds = new Set(query.kinds);
15207
+ function lstatPlainDirectory(dir) {
15208
+ try {
15209
+ const stat2 = fs3.lstatSync(dir);
15210
+ if (!stat2.isDirectory() || stat2.isSymbolicLink()) {
15211
+ return;
15212
+ }
15213
+ return stat2;
15214
+ } catch {
15215
+ return;
15216
+ }
15217
+ }
15218
+ function findEntryDirs(cacheRoot) {
14543
15219
  const candidates = [];
14544
- for (const record of filterActive(records, asOf)) {
14545
- if (!kinds.has(record.kind) || !matchesScope(record, query)) {
15220
+ for (const topEntry of readDirectoryEntries(cacheRoot)) {
15221
+ const topPath = path3.join(cacheRoot, topEntry.name);
15222
+ const topStat = lstatPlainDirectory(topPath);
15223
+ if (topStat === undefined) {
14546
15224
  continue;
14547
15225
  }
14548
- const embedding = deps.embeddings.get(record.id);
14549
- if (embedding === undefined) {
15226
+ if (!topEntry.name.startsWith("@")) {
15227
+ candidates.push({
15228
+ specDir: topEntry.name,
15229
+ absolutePath: topPath,
15230
+ stat: topStat
15231
+ });
14550
15232
  continue;
14551
15233
  }
14552
- const similarity = cosineSimilarity(queryEmbedding, embedding);
14553
- if (similarity < query.minSimilarity) {
14554
- continue;
15234
+ for (const packageEntry of readDirectoryEntries(topPath)) {
15235
+ const packagePath = path3.join(topPath, packageEntry.name);
15236
+ const packageStat = lstatPlainDirectory(packagePath);
15237
+ if (packageStat === undefined) {
15238
+ continue;
15239
+ }
15240
+ candidates.push({
15241
+ specDir: `${topEntry.name}/${packageEntry.name}`,
15242
+ absolutePath: packagePath,
15243
+ stat: packageStat
15244
+ });
14555
15245
  }
14556
- candidates.push({ record, similarity });
14557
15246
  }
14558
- const signals = {
14559
- now: deps.now,
14560
- ...deps.signals ?? DEFAULT_RANK_SIGNALS
14561
- };
14562
- return rankRecall(candidates, signals).slice(0, query.limit);
15247
+ return candidates.sort((a, b) => a.specDir.localeCompare(b.specDir));
14563
15248
  }
14564
-
14565
- // src/memory/traverse.ts
14566
- function inOwnerScope(record, ownerKey, includeShared) {
14567
- if (record.ownerKey === ownerKey) {
14568
- return true;
14569
- }
14570
- return includeShared && record.ownerKey === SHARED_OWNER_KEY;
14571
- }
14572
- function traverseRelations(records, query) {
14573
- const includeShared = query.includeShared ?? true;
14574
- const adjacency = new Map;
14575
- for (const record of records) {
14576
- if (record.kind !== "relation") {
14577
- continue;
14578
- }
14579
- if (!isActiveAt(record, query.asOf)) {
14580
- continue;
14581
- }
14582
- if (!inOwnerScope(record, query.ownerKey, includeShared)) {
15249
+ async function listEntries(cacheRoot) {
15250
+ const entries = [];
15251
+ for (const candidate of findEntryDirs(cacheRoot)) {
15252
+ const { specDir, absolutePath, stat: stat2 } = candidate;
15253
+ if (!specDir.startsWith(SCOPE_PREFIX2))
14583
15254
  continue;
15255
+ const mtimeIso = stat2.mtime.toISOString();
15256
+ const entryPkg = readJsonSafe(path3.join(absolutePath, "package.json"));
15257
+ const deps = entryPkg?.dependencies;
15258
+ let pinnedVersion = "";
15259
+ if (deps !== null && typeof deps === "object" && !Array.isArray(deps)) {
15260
+ const depsObj = deps;
15261
+ const pinned = depsObj[SCOPE_PREFIX2];
15262
+ pinnedVersion = typeof pinned === "string" ? pinned : "";
14584
15263
  }
14585
- const list = adjacency.get(record.from);
14586
- if (list === undefined) {
14587
- adjacency.set(record.from, [record]);
14588
- } else {
14589
- list.push(record);
14590
- }
14591
- }
14592
- const visited = new Set([query.seed]);
14593
- const nodes = [{ id: query.seed, depth: 0 }];
14594
- const edges = [];
14595
- let frontier = [query.seed];
14596
- for (let depth = 0;depth < query.maxDepth; depth += 1) {
14597
- const nextFrontier = [];
14598
- for (const nodeId of frontier) {
14599
- const outgoing = adjacency.get(nodeId) ?? [];
14600
- for (const edge of outgoing) {
14601
- if (visited.has(edge.to)) {
14602
- continue;
14603
- }
14604
- visited.add(edge.to);
14605
- const childDepth = depth + 1;
14606
- nodes.push({ id: edge.to, depth: childDepth });
14607
- edges.push({
14608
- from: edge.from,
14609
- to: edge.to,
14610
- predicate: edge.predicate,
14611
- depth: childDepth
14612
- });
14613
- nextFrontier.push(edge.to);
14614
- }
14615
- }
14616
- frontier = nextFrontier;
15264
+ const installedPkg = readJsonSafe(path3.join(absolutePath, "node_modules", ...SCOPE_SEGMENTS, "package.json"));
15265
+ const installedVersion = extractVersion(installedPkg);
15266
+ entries.push({
15267
+ specDir,
15268
+ absolutePath,
15269
+ pinnedVersion,
15270
+ installedVersion,
15271
+ mtimeIso
15272
+ });
14617
15273
  }
14618
- return { seed: query.seed, nodes, edges };
15274
+ return entries;
14619
15275
  }
14620
- function seedNodeId(record) {
14621
- return record.kind === "entity" ? record.canonicalName : undefined;
15276
+ async function deleteEntry(entryPath) {
15277
+ fs3.rmSync(entryPath, { recursive: true, force: true });
14622
15278
  }
14623
- function recallGraph(records, queryEmbedding, query, options, deps) {
14624
- const seeds = recall(records, queryEmbedding, query, deps);
14625
- const asOf = query.asOf ?? deps.now;
14626
- const includeShared = options.includeShared ?? query.includeShared;
14627
- const traversals = [];
14628
- for (const result of seeds) {
14629
- const seed = seedNodeId(result.record);
14630
- if (seed === undefined) {
14631
- continue;
14632
- }
14633
- traversals.push({
14634
- seedRecordId: result.record.id,
14635
- traversal: traverseRelations(records, {
14636
- seed,
14637
- ownerKey: query.ownerKey,
14638
- asOf,
14639
- maxDepth: options.maxDepth,
14640
- includeShared
14641
- })
14642
- });
15279
+ async function isReparsePath(entryPath) {
15280
+ try {
15281
+ const stat2 = await fs3.promises.lstat(entryPath);
15282
+ return stat2.isSymbolicLink();
15283
+ } catch {
15284
+ return true;
14643
15285
  }
14644
- return { recall: seeds, traversals };
14645
15286
  }
15287
+ var realCacheAdapter = {
15288
+ resolveCacheRoot,
15289
+ listEntries,
15290
+ deleteEntry,
15291
+ isReparsePath
15292
+ };
14646
15293
 
14647
- // src/memory/merge.ts
14648
- var SEP = "\x00";
14649
- function factSlotKey(fact) {
14650
- return [fact.subject, fact.predicate, fact.ownerKey].join(SEP);
14651
- }
14652
- function factValueKey(fact) {
14653
- return [factSlotKey(fact), fact.object].join(SEP);
14654
- }
14655
- function preferenceSlotKey(preference) {
14656
- return [preference.category, preference.ownerKey].join(SEP);
14657
- }
14658
- function preferenceValueKey(preference) {
14659
- return [preferenceSlotKey(preference), preference.preference].join(SEP);
15294
+ // src/orchestrator/rosterPersistence.ts
15295
+ function rosterFence(roster) {
15296
+ const data = { universe: roster.universe, entries: roster.entries };
15297
+ return `\`\`\`json
15298
+ ${JSON.stringify(data, null, 2)}
15299
+ \`\`\``;
14660
15300
  }
14661
- function entitySlotKey(entity) {
14662
- return [entity.canonicalName, entity.type, entity.ownerKey].join(SEP);
15301
+ function serializeRoster(roster) {
15302
+ return `# openteam roster
15303
+
15304
+ Universe: ${roster.universe}
15305
+
15306
+ ${rosterFence(roster)}
15307
+ `;
14663
15308
  }
14664
- function relationSlotKey(relation) {
14665
- return [relation.from, relation.predicate, relation.ownerKey].join(SEP);
15309
+ async function persistRoster(storage, roster, path4 = OPENTEAM_ROSTER_PATH) {
15310
+ await storage.write(path4, serializeRoster(roster));
15311
+ return path4;
14666
15312
  }
14667
- function relationValueKey(relation) {
14668
- return [relationSlotKey(relation), relation.to].join(SEP);
15313
+ async function loadRosterFile(storage, path4 = OPENTEAM_ROSTER_PATH) {
15314
+ const content = await storage.read(path4);
15315
+ if (content === undefined) {
15316
+ return { status: "absent", path: path4 };
15317
+ }
15318
+ const parsed = parseRosterResult(content);
15319
+ if (!parsed.ok) {
15320
+ return { status: "invalid", path: path4, error: parsed.error };
15321
+ }
15322
+ return {
15323
+ status: "loaded",
15324
+ path: path4,
15325
+ roster: enforceGuaranteedRoles(parsed.roster)
15326
+ };
14669
15327
  }
14670
- function coalesceFact(existing, incoming) {
14671
- const window = coalesceWindow(existing, incoming);
15328
+ async function loadOrGenerateRoster(storage, generate, path4 = OPENTEAM_ROSTER_PATH) {
15329
+ const existing = await loadRosterFile(storage, path4);
15330
+ if (existing.status === "loaded") {
15331
+ return { roster: existing.roster, regenerated: false };
15332
+ }
15333
+ const roster = enforceGuaranteedRoles(await generate());
15334
+ if (existing.status === "absent") {
15335
+ await persistRoster(storage, roster, path4);
15336
+ return { roster, regenerated: true };
15337
+ }
14672
15338
  return {
14673
- ...existing,
14674
- validFrom: window.validFrom,
14675
- validUntil: window.validUntil,
14676
- confidence: Math.max(existing.confidence, incoming.confidence)
15339
+ roster,
15340
+ regenerated: true,
15341
+ recoveredFromInvalidPersisted: true,
15342
+ invalidPersistedRosterError: existing.error.message
14677
15343
  };
14678
15344
  }
14679
- function applyFact(state, incoming, now) {
14680
- const incomingSlot = factSlotKey(incoming);
14681
- const incomingValue = factValueKey(incoming);
14682
- const superseded = [];
14683
- const records = [];
14684
- let reaffirmed = false;
14685
- for (const fact of state) {
14686
- if (!isActiveAt(fact, now)) {
14687
- records.push(fact);
14688
- continue;
14689
- }
14690
- if (factValueKey(fact) === incomingValue) {
14691
- records.push(coalesceFact(fact, incoming));
14692
- reaffirmed = true;
14693
- continue;
14694
- }
14695
- if (factSlotKey(fact) === incomingSlot) {
14696
- const dead = {
14697
- ...fact,
14698
- invalidatedAt: now,
14699
- supersededBy: incoming.id
14700
- };
14701
- superseded.push(dead);
14702
- records.push(dead);
14703
- continue;
14704
- }
14705
- records.push(fact);
15345
+
15346
+ // src/memory/types.ts
15347
+ import { z as z21 } from "zod";
15348
+ var SHARED_OWNER_KEY = "*";
15349
+ var OwnerKeySchema = z21.string().min(1);
15350
+ var MemoryKindSchema = z21.enum(["fact", "preference", "entity"]);
15351
+ var MemoryBaseSchema = z21.object({
15352
+ id: z21.string().min(1),
15353
+ ownerKey: OwnerKeySchema,
15354
+ confidence: z21.number().min(0).max(1),
15355
+ validFrom: z21.number().finite(),
15356
+ validUntil: z21.number().finite().nullable().default(null),
15357
+ createdAt: z21.number().finite(),
15358
+ invalidatedAt: z21.number().finite().nullable().default(null),
15359
+ sourceHash: z21.string().min(1),
15360
+ supersededBy: z21.string().min(1).nullable().default(null)
15361
+ }).strict();
15362
+ var FactSchema = MemoryBaseSchema.extend({
15363
+ kind: z21.literal("fact"),
15364
+ subject: z21.string().min(1),
15365
+ predicate: z21.string().min(1),
15366
+ object: z21.string().min(1),
15367
+ category: z21.string().min(1).nullable().default(null)
15368
+ });
15369
+ var PreferenceSchema = MemoryBaseSchema.extend({
15370
+ kind: z21.literal("preference"),
15371
+ category: z21.string().min(1),
15372
+ preference: z21.string().min(1),
15373
+ context: z21.string().min(1).nullable().default(null),
15374
+ lastAccessedAt: z21.number().finite().nullable().default(null),
15375
+ accessCount: z21.number().int().min(0).default(0)
15376
+ });
15377
+ var EntitySchema = MemoryBaseSchema.extend({
15378
+ kind: z21.literal("entity"),
15379
+ canonicalName: z21.string().min(1),
15380
+ type: z21.string().min(1),
15381
+ aliases: z21.array(z21.string().min(1)).default([])
15382
+ });
15383
+ var RelationSchema = MemoryBaseSchema.extend({
15384
+ kind: z21.literal("relation"),
15385
+ from: z21.string().min(1),
15386
+ to: z21.string().min(1),
15387
+ predicate: z21.string().min(1),
15388
+ annotation: z21.string().min(1).nullable().default(null)
15389
+ });
15390
+ var MemoryRecordSchema = z21.discriminatedUnion("kind", [
15391
+ FactSchema,
15392
+ PreferenceSchema,
15393
+ EntitySchema,
15394
+ RelationSchema
15395
+ ]);
15396
+ var RecallQuerySchema = z21.object({
15397
+ ownerKey: OwnerKeySchema,
15398
+ kinds: z21.array(MemoryKindSchema).default(["fact", "preference", "entity"]),
15399
+ asOf: z21.number().finite().nullable().default(null),
15400
+ limit: z21.number().int().positive().default(8),
15401
+ minSimilarity: z21.number().min(0).max(1).default(0.2),
15402
+ includeShared: z21.boolean().default(true)
15403
+ }).strict();
15404
+
15405
+ // src/memory/rank.ts
15406
+ var DAY_MS = 86400000;
15407
+ var DEFAULT_RANK_SIGNALS = {
15408
+ similarityWeight: 1,
15409
+ recencyWeight: 0.2,
15410
+ accessWeight: 0.1,
15411
+ recencyHalfLifeMs: 14 * DAY_MS
15412
+ };
15413
+ function recencyTimestamp(record) {
15414
+ if (record.kind === "preference" && record.lastAccessedAt !== null) {
15415
+ return record.lastAccessedAt;
14706
15416
  }
14707
- if (!reaffirmed) {
14708
- records.push(incoming);
15417
+ return record.createdAt;
15418
+ }
15419
+ function recencyScore(record, signals) {
15420
+ const ageMs = Math.max(0, signals.now - recencyTimestamp(record));
15421
+ return 0.5 ** (ageMs / signals.recencyHalfLifeMs);
15422
+ }
15423
+ function accessScore(record) {
15424
+ if (record.kind !== "preference") {
15425
+ return 0;
14709
15426
  }
14710
- return { records, superseded };
15427
+ return 1 - 1 / (1 + record.accessCount);
14711
15428
  }
14712
- function applyPreference(state, incoming, now) {
14713
- const incomingSlot = preferenceSlotKey(incoming);
14714
- const incomingValue = preferenceValueKey(incoming);
14715
- const superseded = [];
15429
+ function scoreCandidate(candidate, signals) {
15430
+ return signals.similarityWeight * candidate.similarity + signals.recencyWeight * recencyScore(candidate.record, signals) + signals.accessWeight * accessScore(candidate.record);
15431
+ }
15432
+ function rankRecall(candidates, signals) {
15433
+ return candidates.map((candidate) => ({
15434
+ ...candidate,
15435
+ score: scoreCandidate(candidate, signals)
15436
+ })).sort((a, b) => {
15437
+ if (b.score !== a.score) {
15438
+ return b.score - a.score;
15439
+ }
15440
+ return a.record.id < b.record.id ? -1 : a.record.id > b.record.id ? 1 : 0;
15441
+ });
15442
+ }
15443
+
15444
+ // src/memory/temporal.ts
15445
+ function isTransactionActiveAt(record, asOf) {
15446
+ return record.invalidatedAt === null || record.invalidatedAt > asOf;
15447
+ }
15448
+ function isValidAt(record, asOf) {
15449
+ if (record.validFrom > asOf) {
15450
+ return false;
15451
+ }
15452
+ return record.validUntil === null || record.validUntil > asOf;
15453
+ }
15454
+ function isActiveAt(record, asOf) {
15455
+ return isTransactionActiveAt(record, asOf) && isValidAt(record, asOf);
15456
+ }
15457
+ function filterActive(records, asOf) {
15458
+ return records.filter((record) => isActiveAt(record, asOf));
15459
+ }
15460
+ function coalesceWindow(a, b) {
15461
+ const validFrom = Math.min(a.validFrom, b.validFrom);
15462
+ const validUntil = a.validUntil === null || b.validUntil === null ? null : Math.max(a.validUntil, b.validUntil);
15463
+ return { validFrom, validUntil };
15464
+ }
15465
+
15466
+ // src/memory/vector.ts
15467
+ function dot(a, b) {
15468
+ if (a.length !== b.length) {
15469
+ throw new Error(`dimension mismatch: ${a.length} vs ${b.length}`);
15470
+ }
15471
+ let sum = 0;
15472
+ for (let i = 0;i < a.length; i += 1) {
15473
+ sum += a[i] * b[i];
15474
+ }
15475
+ return sum;
15476
+ }
15477
+ function norm(a) {
15478
+ let sum = 0;
15479
+ for (let i = 0;i < a.length; i += 1) {
15480
+ const value = a[i];
15481
+ sum += value * value;
15482
+ }
15483
+ return Math.sqrt(sum);
15484
+ }
15485
+ function cosineSimilarity(a, b) {
15486
+ const denom = norm(a) * norm(b);
15487
+ if (denom === 0) {
15488
+ return 0;
15489
+ }
15490
+ return dot(a, b) / denom;
15491
+ }
15492
+
15493
+ // src/memory/recall.ts
15494
+ function matchesScope(record, query) {
15495
+ if (record.ownerKey === query.ownerKey) {
15496
+ return true;
15497
+ }
15498
+ return query.includeShared && record.ownerKey === SHARED_OWNER_KEY;
15499
+ }
15500
+ function recall(records, queryEmbedding, query, deps) {
15501
+ const asOf = query.asOf ?? deps.now;
15502
+ const kinds = new Set(query.kinds);
15503
+ const candidates = [];
15504
+ for (const record of filterActive(records, asOf)) {
15505
+ if (!kinds.has(record.kind) || !matchesScope(record, query)) {
15506
+ continue;
15507
+ }
15508
+ const embedding = deps.embeddings.get(record.id);
15509
+ if (embedding === undefined) {
15510
+ continue;
15511
+ }
15512
+ const similarity = cosineSimilarity(queryEmbedding, embedding);
15513
+ if (similarity < query.minSimilarity) {
15514
+ continue;
15515
+ }
15516
+ candidates.push({ record, similarity });
15517
+ }
15518
+ const signals = {
15519
+ now: deps.now,
15520
+ ...deps.signals ?? DEFAULT_RANK_SIGNALS
15521
+ };
15522
+ return rankRecall(candidates, signals).slice(0, query.limit);
15523
+ }
15524
+
15525
+ // src/memory/merge.ts
15526
+ var SEP = "\x00";
15527
+ function factSlotKey(fact) {
15528
+ return [fact.subject, fact.predicate, fact.ownerKey].join(SEP);
15529
+ }
15530
+ function factValueKey(fact) {
15531
+ return [factSlotKey(fact), fact.object].join(SEP);
15532
+ }
15533
+ function preferenceSlotKey(preference) {
15534
+ return [preference.category, preference.ownerKey].join(SEP);
15535
+ }
15536
+ function preferenceValueKey(preference) {
15537
+ return [preferenceSlotKey(preference), preference.preference].join(SEP);
15538
+ }
15539
+ function entitySlotKey(entity) {
15540
+ return [entity.canonicalName, entity.type, entity.ownerKey].join(SEP);
15541
+ }
15542
+ function relationSlotKey(relation) {
15543
+ return [relation.from, relation.predicate, relation.ownerKey].join(SEP);
15544
+ }
15545
+ function relationValueKey(relation) {
15546
+ return [relationSlotKey(relation), relation.to].join(SEP);
15547
+ }
15548
+ function coalesceFact(existing, incoming) {
15549
+ const window = coalesceWindow(existing, incoming);
15550
+ return {
15551
+ ...existing,
15552
+ validFrom: window.validFrom,
15553
+ validUntil: window.validUntil,
15554
+ confidence: Math.max(existing.confidence, incoming.confidence)
15555
+ };
15556
+ }
15557
+ function applyFact(state, incoming, now) {
15558
+ const incomingSlot = factSlotKey(incoming);
15559
+ const incomingValue = factValueKey(incoming);
15560
+ const superseded = [];
15561
+ const records = [];
15562
+ let reaffirmed = false;
15563
+ for (const fact of state) {
15564
+ if (!isActiveAt(fact, now)) {
15565
+ records.push(fact);
15566
+ continue;
15567
+ }
15568
+ if (factValueKey(fact) === incomingValue) {
15569
+ records.push(coalesceFact(fact, incoming));
15570
+ reaffirmed = true;
15571
+ continue;
15572
+ }
15573
+ if (factSlotKey(fact) === incomingSlot) {
15574
+ const dead = {
15575
+ ...fact,
15576
+ invalidatedAt: now,
15577
+ supersededBy: incoming.id
15578
+ };
15579
+ superseded.push(dead);
15580
+ records.push(dead);
15581
+ continue;
15582
+ }
15583
+ records.push(fact);
15584
+ }
15585
+ if (!reaffirmed) {
15586
+ records.push(incoming);
15587
+ }
15588
+ return { records, superseded };
15589
+ }
15590
+ function applyPreference(state, incoming, now) {
15591
+ const incomingSlot = preferenceSlotKey(incoming);
15592
+ const incomingValue = preferenceValueKey(incoming);
15593
+ const superseded = [];
14716
15594
  const records = [];
14717
15595
  let reaffirmed = false;
14718
15596
  for (const preference of state) {
@@ -15076,71 +15954,550 @@ async function recallMemory(deps, queryText, query) {
15076
15954
  return recall(recordRead.records, queryEmbedding, query, recallDeps);
15077
15955
  }
15078
15956
 
15079
- // src/mcp/semanticRecall.ts
15080
- function buildSemanticRecall(deps, policy) {
15081
- return async ({ queryText, kinds, limit }) => {
15082
- const query = RecallQuerySchema.parse({
15083
- ownerKey: policy.ownerKey,
15084
- kinds,
15085
- asOf: policy.asOf ?? null,
15086
- limit,
15087
- minSimilarity: policy.minSimilarity,
15088
- includeShared: policy.includeShared
15089
- });
15090
- const recallDeps = deps.signals === undefined ? { index: deps.index, embedder: deps.embedder, now: deps.now() } : {
15091
- index: deps.index,
15092
- embedder: deps.embedder,
15093
- now: deps.now(),
15094
- signals: deps.signals
15095
- };
15096
- return recallMemory(recallDeps, queryText, query);
15097
- };
15098
- }
15099
- var DEFAULT_TRAVERSAL_DEPTH = 3;
15100
- function buildRelationTraversal(deps, policy) {
15101
- return async ({ seed, maxDepth }) => {
15102
- const asOf = policy.asOf ?? deps.now();
15103
- return traverseRelations(deps.index.allRecords(), {
15104
- seed,
15105
- ownerKey: policy.ownerKey,
15106
- asOf,
15107
- maxDepth: maxDepth ?? DEFAULT_TRAVERSAL_DEPTH,
15108
- includeShared: policy.includeShared
15109
- });
15110
- };
15111
- }
15112
- function buildGraphRecall(deps, policy) {
15113
- return async ({ queryText, limit, maxDepth }) => {
15114
- const query = RecallQuerySchema.parse({
15115
- ownerKey: policy.ownerKey,
15116
- kinds: ["fact", "preference", "entity"],
15117
- asOf: policy.asOf ?? null,
15118
- limit,
15119
- minSimilarity: policy.minSimilarity,
15120
- includeShared: policy.includeShared
15121
- });
15122
- const [queryEmbedding] = await deps.embedder([queryText]);
15123
- if (queryEmbedding === undefined) {
15124
- return { recall: [], traversals: [] };
15957
+ // src/plugin/orchestrateTool.ts
15958
+ import { tool } from "@opencode-ai/plugin";
15959
+ import { z as z22 } from "zod";
15960
+ var _pluginWarnedRoles = new Set;
15961
+ var RoleAssignmentSchema = z22.object({
15962
+ roleID: z22.string().min(1).describe("Role identifier from the roster"),
15963
+ prompt: z22.string().min(1).describe("Work prompt for this role"),
15964
+ title: z22.string().min(1).optional().describe("Human-readable title"),
15965
+ dependsOn: z22.array(z22.string().min(1)).optional().describe("Role IDs this assignment depends on (DAG edges)")
15966
+ }).strict();
15967
+ var OrchestratePayloadSchema = z22.object({
15968
+ assignments: z22.array(RoleAssignmentSchema).min(1).describe("Role assignments to distribute"),
15969
+ parentSessionID: z22.string().min(1).optional().describe("Parent session for subsessions"),
15970
+ directory: z22.string().min(1).optional().describe("Working directory override")
15971
+ }).strict();
15972
+ function adaptSdkClient(sdk) {
15973
+ return {
15974
+ session: {
15975
+ create: async (args) => {
15976
+ const result = await sdk.session.create(args);
15977
+ const out = {};
15978
+ if (result.data !== undefined) {
15979
+ out.data = result.data;
15980
+ }
15981
+ if (result.error !== undefined) {
15982
+ out.error = result.error;
15983
+ }
15984
+ return out;
15985
+ },
15986
+ prompt: async (args) => {
15987
+ const result = await sdk.session.prompt(args);
15988
+ const out = {};
15989
+ if (result.data !== undefined) {
15990
+ out.data = result.data;
15991
+ }
15992
+ if (result.error !== undefined) {
15993
+ out.error = result.error;
15994
+ }
15995
+ return out;
15996
+ }
15125
15997
  }
15126
- const recallDeps = deps.signals === undefined ? { embeddings: deps.index.embeddingMap(), now: deps.now() } : {
15127
- embeddings: deps.index.embeddingMap(),
15128
- now: deps.now(),
15129
- signals: deps.signals
15130
- };
15131
- return recallGraph(deps.index.allRecords(), queryEmbedding, query, {
15132
- maxDepth: maxDepth ?? DEFAULT_TRAVERSAL_DEPTH,
15133
- includeShared: policy.includeShared
15134
- }, recallDeps);
15135
15998
  };
15136
15999
  }
15137
16000
 
15138
- // src/mcp/teamMemory.ts
15139
- function buildTeamMemory(events) {
15140
- const aggregate = aggregateEvents(events);
15141
- return {
15142
- sessions: aggregate.sessions,
15143
- totals: aggregate.totals,
16001
+ // src/local/embeddings.ts
16002
+ function parseEmbeddingsResponse(payload) {
16003
+ if (typeof payload !== "object" || payload === null || !Array.isArray(payload.data)) {
16004
+ throw new Error("Malformed /embeddings response: missing 'data'");
16005
+ }
16006
+ const data = payload.data;
16007
+ return data.map((entry, index) => {
16008
+ const embedding = typeof entry === "object" && entry !== null ? entry.embedding : undefined;
16009
+ if (!Array.isArray(embedding) || !embedding.every((n) => typeof n === "number" && Number.isFinite(n))) {
16010
+ throw new Error(`Malformed /embeddings response: invalid vector at index ${index}`);
16011
+ }
16012
+ return embedding;
16013
+ });
16014
+ }
16015
+ async function embedTexts(request, deps) {
16016
+ if (request.texts.length === 0) {
16017
+ return [];
16018
+ }
16019
+ let response;
16020
+ try {
16021
+ response = await deps.fetch(embeddingsEndpoint(request.baseURL), {
16022
+ method: "POST",
16023
+ headers: {
16024
+ "content-type": "application/json",
16025
+ accept: "application/json"
16026
+ },
16027
+ body: JSON.stringify({
16028
+ model: request.modelID,
16029
+ input: [...request.texts]
16030
+ })
16031
+ });
16032
+ } catch (error) {
16033
+ throw new Error(`Local embeddings failed: ${errorMessage(error)}`);
16034
+ }
16035
+ if (!response.ok) {
16036
+ throw new Error(`Local embeddings failed: HTTP ${response.status} on /embeddings`);
16037
+ }
16038
+ let payload;
16039
+ try {
16040
+ payload = await response.json();
16041
+ } catch (error) {
16042
+ throw new Error(`Local embeddings failed: ${errorMessage(error)}`);
16043
+ }
16044
+ const vectors = parseEmbeddingsResponse(payload);
16045
+ if (vectors.length !== request.texts.length) {
16046
+ throw new Error(`Local embeddings returned ${vectors.length} vectors for ${request.texts.length} texts`);
16047
+ }
16048
+ return vectors;
16049
+ }
16050
+ function createLocalEmbedder(request, deps) {
16051
+ return (texts) => embedTexts({ ...request, texts }, deps);
16052
+ }
16053
+
16054
+ // src/memory/embedText.ts
16055
+ function normalize(parts) {
16056
+ return parts.map((part) => part.trim()).filter((part) => part.length > 0).join(" ");
16057
+ }
16058
+ function recordEmbeddingText(record) {
16059
+ if (record.kind === "fact") {
16060
+ return normalize([
16061
+ record.subject,
16062
+ record.predicate,
16063
+ record.object,
16064
+ record.category ?? ""
16065
+ ]);
16066
+ }
16067
+ if (record.kind === "preference") {
16068
+ return normalize([
16069
+ record.category,
16070
+ record.preference,
16071
+ record.context ?? ""
16072
+ ]);
16073
+ }
16074
+ if (record.kind === "relation") {
16075
+ return normalize([
16076
+ record.from,
16077
+ record.predicate,
16078
+ record.to,
16079
+ record.annotation ?? ""
16080
+ ]);
16081
+ }
16082
+ return normalize([record.canonicalName, record.type, ...record.aliases]);
16083
+ }
16084
+
16085
+ // src/storage/index/memoryEmbeddings.ts
16086
+ async function ensureMemoryEmbeddings(deps) {
16087
+ const { index, embedder, model } = deps;
16088
+ const warn = deps.warn ?? console.warn;
16089
+ const storedModel = index.embeddingModel();
16090
+ const modelChanged = storedModel !== null && storedModel !== model;
16091
+ if (modelChanged) {
16092
+ index.clearEmbeddings();
16093
+ }
16094
+ index.setEmbeddingModel(model);
16095
+ const records = index.allRecords();
16096
+ index.pruneEmbeddings();
16097
+ const have = modelChanged ? new Set : new Set(index.embeddingMap().keys());
16098
+ const todo = records.filter((record) => !have.has(record.id));
16099
+ if (todo.length === 0) {
16100
+ return { embedded: 0, failed: 0, modelChanged };
16101
+ }
16102
+ let vectors;
16103
+ try {
16104
+ vectors = await embedder(todo.map(recordEmbeddingText));
16105
+ } catch (error) {
16106
+ const reason = error instanceof Error ? error.message : String(error);
16107
+ warn(`[openteam] could not compute embeddings for ${todo.length} memory record(s): ${reason}. ` + "Recall over them is unavailable until the local runtime is reachable.");
16108
+ return { embedded: 0, failed: todo.length, modelChanged };
16109
+ }
16110
+ if (vectors.length !== todo.length) {
16111
+ warn(`[openteam] could not compute embeddings for ${todo.length} memory record(s): ` + `the embedder returned ${vectors.length} vector(s). Recall over them is unavailable.`);
16112
+ return { embedded: 0, failed: todo.length, modelChanged };
16113
+ }
16114
+ const entries = todo.map((record, position) => ({
16115
+ recordId: record.id,
16116
+ vector: vectors[position]
16117
+ }));
16118
+ index.putEmbeddings(entries);
16119
+ return { embedded: entries.length, failed: 0, modelChanged };
16120
+ }
16121
+
16122
+ // src/storage/index/memoryRuntime.ts
16123
+ async function buildMemoryRuntimeFromConfig(config, deps, options = {}) {
16124
+ const semantic = resolveMemorySemantic(config);
16125
+ if (!semantic.enabled || semantic.embeddings.provider === "none") {
16126
+ return;
16127
+ }
16128
+ if (options.requireInjectionEnabled === true && !semantic.injection.enabled) {
16129
+ return;
16130
+ }
16131
+ const runtime = config.local.runtimes.find((rt) => rt.enabled && rt.baseURL !== undefined);
16132
+ if (runtime?.baseURL === undefined) {
16133
+ return;
16134
+ }
16135
+ const modelID = semantic.embeddings.model?.modelID ?? runtime.defaultModel.modelID;
16136
+ const openDatabase = deps.openDatabase ?? createBunSqliteDatabase;
16137
+ const makeEmbedder = deps.createEmbedder ?? createLocalEmbedder;
16138
+ const fetchImpl = deps.fetch ?? globalThis.fetch;
16139
+ const now = deps.now ?? (() => Date.now());
16140
+ const warn = deps.warn ?? console.warn;
16141
+ try {
16142
+ const db = await openDatabase(semantic.indexPath);
16143
+ const index = createMemoryIndex(db, warn);
16144
+ const rebuildResult = await rebuildMemoryIndexFromStorage(index, semantic.logPath, { storage: deps.storage });
16145
+ if (rebuildResult.rejectedLines > 0) {
16146
+ warn(`[openteam] memory log rejected ${rebuildResult.rejectedLines} line(s); semantic memory may be incomplete.`);
16147
+ }
16148
+ const embedder = makeEmbedder({ baseURL: runtime.baseURL, modelID }, { fetch: fetchImpl });
16149
+ await ensureMemoryEmbeddings({ index, embedder, model: modelID, warn });
16150
+ return {
16151
+ index,
16152
+ embedder,
16153
+ now,
16154
+ ownerKey: semantic.scope === "user" ? "user" : "project",
16155
+ semantic
16156
+ };
16157
+ } catch {
16158
+ return;
16159
+ }
16160
+ }
16161
+ // package.json
16162
+ var package_default = {
16163
+ name: "@jmanuelcorral/openteam",
16164
+ version: "0.16.2",
16165
+ packageManager: "bun@1.3.14",
16166
+ description: "Cost-aware, local-first routing plugin for opencode with cheapest-capable frontier fallback and multi-agent orchestration.",
16167
+ license: "MIT",
16168
+ author: "Jose Manuel Corral",
16169
+ repository: {
16170
+ type: "git",
16171
+ url: "git+https://github.com/jmanuelcorral/openteam.git"
16172
+ },
16173
+ homepage: "https://github.com/jmanuelcorral/openteam#readme",
16174
+ bugs: {
16175
+ url: "https://github.com/jmanuelcorral/openteam/issues"
16176
+ },
16177
+ keywords: [
16178
+ "opencode",
16179
+ "opencode-plugin",
16180
+ "llm",
16181
+ "routing",
16182
+ "local-llm",
16183
+ "ollama",
16184
+ "lm-studio",
16185
+ "foundry-local",
16186
+ "cost-optimization",
16187
+ "multi-agent"
16188
+ ],
16189
+ engines: {
16190
+ bun: ">=1.3",
16191
+ node: "^22.22.2 || ^24.15.0 || >=26.0.0"
16192
+ },
16193
+ type: "module",
16194
+ main: "./dist/index.js",
16195
+ module: "./dist/index.js",
16196
+ types: "./dist/index.d.ts",
16197
+ bin: {
16198
+ openteam: "./dist/cli.js"
16199
+ },
16200
+ exports: {
16201
+ ".": {
16202
+ types: "./dist/index.d.ts",
16203
+ import: "./dist/index.js"
16204
+ },
16205
+ "./package.json": "./package.json"
16206
+ },
16207
+ files: [
16208
+ "dist",
16209
+ "README.md",
16210
+ "LICENSE",
16211
+ "AGENTS.md",
16212
+ ".opencode/openteam.example.json",
16213
+ ".opencode/command/openteam.md"
16214
+ ],
16215
+ publishConfig: {
16216
+ access: "public",
16217
+ registry: "https://registry.npmjs.org/"
16218
+ },
16219
+ trustedDependencies: [],
16220
+ sideEffects: false,
16221
+ scripts: {
16222
+ prebuild: "bun run clean",
16223
+ build: "bun run build:js && bun run build:cli && bun run build:types && bun run build:certificates",
16224
+ "build:js": "bun build ./src/index.ts --target=node --format=esm --outfile=dist/index.js --external @opencode-ai/plugin --external @opencode-ai/sdk --external zod --external @opentelemetry/api --external @opentelemetry/sdk-trace-base --external @opentelemetry/exporter-trace-otlp-http --external @opentelemetry/resources --external @langchain/langgraph",
16225
+ "build:cli": 'bun build ./src/cli.ts --target=node --format=esm --outfile=dist/cli.js --banner "#!/usr/bin/env node" --external @opencode-ai/plugin --external @opencode-ai/sdk --external zod --external @clack/prompts --external @opentelemetry/api --external @opentelemetry/sdk-trace-base --external @opentelemetry/exporter-trace-otlp-http --external @opentelemetry/resources --external @langchain/langgraph',
16226
+ "build:types": "tsc -p tsconfig.build.json",
16227
+ clean: `node -e "require('node:fs').rmSync('dist', { recursive: true, force: true })"`,
16228
+ test: "bun test",
16229
+ "test:cov": "bun test --coverage",
16230
+ "certify:shadow": "bun run scripts/certify-artifact.ts shadow",
16231
+ "certify:release": "bun run scripts/certify-artifact.ts release",
16232
+ "build:certificates": "bun run certify:shadow && bun run certify:release && bun run scripts/bundle-certificates.ts",
16233
+ "coverage:check": "node scripts/check-coverage.mjs",
16234
+ typecheck: "tsc --noEmit",
16235
+ lint: "biome check .",
16236
+ "format:check": "biome format .",
16237
+ "docs:install": "cd docs && bun install --frozen-lockfile --ignore-scripts",
16238
+ "docs:dev": "cd docs && bun run docs:dev",
16239
+ "docs:build": "cd docs && bun run docs:build",
16240
+ "docs:preview": "cd docs && bun run docs:preview",
16241
+ prepublishOnly: "bun run build",
16242
+ "link:local": "bun run build && npm link",
16243
+ "hooks:install": "git config core.hooksPath .githooks"
16244
+ },
16245
+ dependencies: {
16246
+ "@clack/prompts": "1.7.0",
16247
+ "@langchain/core": "1.2.9",
16248
+ "@langchain/langgraph": "1.4.12",
16249
+ "@opencode-ai/plugin": "1.18.19",
16250
+ "@opencode-ai/sdk": "1.18.19",
16251
+ "@opentelemetry/api": "1.9.1",
16252
+ "@opentelemetry/exporter-trace-otlp-http": "0.221.0",
16253
+ "@opentelemetry/resources": "2.10.0",
16254
+ "@opentelemetry/sdk-trace-base": "2.10.0",
16255
+ zod: "4.4.3"
16256
+ },
16257
+ devDependencies: {
16258
+ "@biomejs/biome": "2.5.9",
16259
+ "@types/bun": "1.3.14",
16260
+ typescript: "7.0.2"
16261
+ }
16262
+ };
16263
+
16264
+ // src/version.ts
16265
+ var PACKAGE_VERSION = package_default.version;
16266
+
16267
+ // src/index.ts
16268
+ function warnSoakEvidenceRejections(result, warn) {
16269
+ if (result.rejectedLines === 0) {
16270
+ return;
16271
+ }
16272
+ (warn ?? console.warn)(`[openteam] soak evidence reader rejected ${result.rejectedLines} observation line(s); cutover gate evidence may be incomplete.`);
16273
+ for (const rejection of result.rejections) {
16274
+ (warn ?? console.warn)(`[openteam] • ${rejection.file}:${rejection.lineNumber}: ${rejection.detail}`);
16275
+ }
16276
+ }
16277
+ var SHADOW_CERT_PATH = "artifacts/graph-shadow-certificate.json";
16278
+ var RELEASE_CERT_PATH = "artifacts/graph-release-certificate.json";
16279
+ var SOAK_CERT_PATH = "artifacts/graph-soak-certificate.json";
16280
+ async function readPackagedCertificate(path4) {
16281
+ try {
16282
+ return await readFile2(new URL(`./certificates/${basename2(path4)}`, import.meta.url), "utf8");
16283
+ } catch {
16284
+ return;
16285
+ }
16286
+ }
16287
+ async function readAndParseCertificate(storage, path4, parse2, digestExtractor, fallbackRead) {
16288
+ try {
16289
+ const content = await storage.read(path4) ?? (fallbackRead === undefined ? undefined : await fallbackRead(path4));
16290
+ if (content === undefined) {
16291
+ return { status: "absent" };
16292
+ }
16293
+ const raw = JSON.parse(content);
16294
+ const cert = parse2(raw);
16295
+ return { status: "valid", digest: digestExtractor(cert) };
16296
+ } catch (error) {
16297
+ if (isMissingFile(error)) {
16298
+ return { status: "absent" };
16299
+ }
16300
+ if (error instanceof ShadowCertificateError || error instanceof ReleaseCertificateError || error instanceof SoakCertificateError) {
16301
+ return { status: "invalid", code: error.code, detail: error.detail };
16302
+ }
16303
+ const msg = error instanceof Error ? error.message : String(error);
16304
+ return { status: "invalid", code: "read-error", detail: msg };
16305
+ }
16306
+ }
16307
+ async function buildGateInput(config, storage, opencodeVersion, warn, packagedRead = readPackagedCertificate) {
16308
+ const shadow = await readAndParseCertificate(storage, SHADOW_CERT_PATH, (raw) => parseShadowCertificate(raw, opencodeVersion), (cert) => shadowCertificateBindingDigest(cert.evidence), packagedRead);
16309
+ const shadowDigest = shadow.status === "valid" ? shadow.digest : "0".repeat(64);
16310
+ const release = await readAndParseCertificate(storage, RELEASE_CERT_PATH, (raw) => parseReleaseCertificate(raw, opencodeVersion, shadowDigest), (cert) => cert.digest, packagedRead);
16311
+ let soakLedgerDigest = "0".repeat(64);
16312
+ try {
16313
+ const recorderPorts = recorderPortsFromStorage(storage);
16314
+ const chainRead = await readAllChains(recorderPorts, SOAK_EVIDENCE_DIR);
16315
+ warnSoakEvidenceRejections(chainRead, warn);
16316
+ soakLedgerDigest = canonicalLedger(chainRead.chains);
16317
+ } catch {}
16318
+ const soak = await readAndParseCertificate(storage, SOAK_CERT_PATH, (raw) => parseSoakCertificate(raw, DEFAULT_SOAK_POLICY, soakLedgerDigest), (cert) => cert.digest);
16319
+ const migration = "complete";
16320
+ const operatorApproval = config.graph?.operatorApproval ?? false;
16321
+ return { shadow, release, soak, migration, operatorApproval };
16322
+ }
16323
+ var OPENCODE_VERSION_UNRESOLVED_REASON = "the opencode version is not determinable outside a running opencode session, " + "so the cutover gate cannot be verified here — the running plugin logs the " + "authoritative gate result and warns loudly on a real denial";
16324
+ async function evaluateCutoverGateForBoundary(config, storage, version) {
16325
+ if (version === undefined) {
16326
+ return { kind: "unevaluable", reason: OPENCODE_VERSION_UNRESOLVED_REASON };
16327
+ }
16328
+ const gateInput = await buildGateInput(config, storage, version, () => {});
16329
+ return { kind: "evaluated", gate: evaluateGraphGate(gateInput) };
16330
+ }
16331
+ function recorderPortsFromStorage(storage) {
16332
+ return {
16333
+ fs: {
16334
+ readFile: async (path4) => {
16335
+ const content = await storage.read(path4);
16336
+ if (content === undefined) {
16337
+ throw Object.assign(new Error(`ENOENT: ${path4}`), { code: "ENOENT" });
16338
+ }
16339
+ return content;
16340
+ },
16341
+ writeFile: (path4, content) => storage.write(path4, content),
16342
+ exists: (path4) => storage.exists(path4),
16343
+ readDir: (path4) => storage.list(path4),
16344
+ mkdir: (path4) => storage.mkdir(path4)
16345
+ },
16346
+ clock: { now: () => new Date().toISOString() },
16347
+ id: { randomUUID: () => crypto.randomUUID() }
16348
+ };
16349
+ }
16350
+ function isMissingFile(error) {
16351
+ return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
16352
+ }
16353
+
16354
+ // src/memory/traverse.ts
16355
+ function inOwnerScope(record, ownerKey, includeShared) {
16356
+ if (record.ownerKey === ownerKey) {
16357
+ return true;
16358
+ }
16359
+ return includeShared && record.ownerKey === SHARED_OWNER_KEY;
16360
+ }
16361
+ function traverseRelations(records, query) {
16362
+ const includeShared = query.includeShared ?? true;
16363
+ const adjacency = new Map;
16364
+ for (const record of records) {
16365
+ if (record.kind !== "relation") {
16366
+ continue;
16367
+ }
16368
+ if (!isActiveAt(record, query.asOf)) {
16369
+ continue;
16370
+ }
16371
+ if (!inOwnerScope(record, query.ownerKey, includeShared)) {
16372
+ continue;
16373
+ }
16374
+ const list = adjacency.get(record.from);
16375
+ if (list === undefined) {
16376
+ adjacency.set(record.from, [record]);
16377
+ } else {
16378
+ list.push(record);
16379
+ }
16380
+ }
16381
+ const visited = new Set([query.seed]);
16382
+ const nodes = [{ id: query.seed, depth: 0 }];
16383
+ const edges = [];
16384
+ let frontier = [query.seed];
16385
+ for (let depth = 0;depth < query.maxDepth; depth += 1) {
16386
+ const nextFrontier = [];
16387
+ for (const nodeId of frontier) {
16388
+ const outgoing = adjacency.get(nodeId) ?? [];
16389
+ for (const edge of outgoing) {
16390
+ if (visited.has(edge.to)) {
16391
+ continue;
16392
+ }
16393
+ visited.add(edge.to);
16394
+ const childDepth = depth + 1;
16395
+ nodes.push({ id: edge.to, depth: childDepth });
16396
+ edges.push({
16397
+ from: edge.from,
16398
+ to: edge.to,
16399
+ predicate: edge.predicate,
16400
+ depth: childDepth
16401
+ });
16402
+ nextFrontier.push(edge.to);
16403
+ }
16404
+ }
16405
+ frontier = nextFrontier;
16406
+ }
16407
+ return { seed: query.seed, nodes, edges };
16408
+ }
16409
+ function seedNodeId(record) {
16410
+ return record.kind === "entity" ? record.canonicalName : undefined;
16411
+ }
16412
+ function recallGraph(records, queryEmbedding, query, options, deps) {
16413
+ const seeds = recall(records, queryEmbedding, query, deps);
16414
+ const asOf = query.asOf ?? deps.now;
16415
+ const includeShared = options.includeShared ?? query.includeShared;
16416
+ const traversals = [];
16417
+ for (const result of seeds) {
16418
+ const seed = seedNodeId(result.record);
16419
+ if (seed === undefined) {
16420
+ continue;
16421
+ }
16422
+ traversals.push({
16423
+ seedRecordId: result.record.id,
16424
+ traversal: traverseRelations(records, {
16425
+ seed,
16426
+ ownerKey: query.ownerKey,
16427
+ asOf,
16428
+ maxDepth: options.maxDepth,
16429
+ includeShared
16430
+ })
16431
+ });
16432
+ }
16433
+ return { recall: seeds, traversals };
16434
+ }
16435
+
16436
+ // src/mcp/semanticRecall.ts
16437
+ function buildSemanticRecall(deps, policy) {
16438
+ return async ({ queryText, kinds, limit }) => {
16439
+ const query = RecallQuerySchema.parse({
16440
+ ownerKey: policy.ownerKey,
16441
+ kinds,
16442
+ asOf: policy.asOf ?? null,
16443
+ limit,
16444
+ minSimilarity: policy.minSimilarity,
16445
+ includeShared: policy.includeShared
16446
+ });
16447
+ const recallDeps = deps.signals === undefined ? { index: deps.index, embedder: deps.embedder, now: deps.now() } : {
16448
+ index: deps.index,
16449
+ embedder: deps.embedder,
16450
+ now: deps.now(),
16451
+ signals: deps.signals
16452
+ };
16453
+ return recallMemory(recallDeps, queryText, query);
16454
+ };
16455
+ }
16456
+ var DEFAULT_TRAVERSAL_DEPTH = 3;
16457
+ function buildRelationTraversal(deps, policy) {
16458
+ return async ({ seed, maxDepth }) => {
16459
+ const asOf = policy.asOf ?? deps.now();
16460
+ return traverseRelations(deps.index.allRecords(), {
16461
+ seed,
16462
+ ownerKey: policy.ownerKey,
16463
+ asOf,
16464
+ maxDepth: maxDepth ?? DEFAULT_TRAVERSAL_DEPTH,
16465
+ includeShared: policy.includeShared
16466
+ });
16467
+ };
16468
+ }
16469
+ function buildGraphRecall(deps, policy) {
16470
+ return async ({ queryText, limit, maxDepth }) => {
16471
+ const query = RecallQuerySchema.parse({
16472
+ ownerKey: policy.ownerKey,
16473
+ kinds: ["fact", "preference", "entity"],
16474
+ asOf: policy.asOf ?? null,
16475
+ limit,
16476
+ minSimilarity: policy.minSimilarity,
16477
+ includeShared: policy.includeShared
16478
+ });
16479
+ const [queryEmbedding] = await deps.embedder([queryText]);
16480
+ if (queryEmbedding === undefined) {
16481
+ return { recall: [], traversals: [] };
16482
+ }
16483
+ const recallDeps = deps.signals === undefined ? { embeddings: deps.index.embeddingMap(), now: deps.now() } : {
16484
+ embeddings: deps.index.embeddingMap(),
16485
+ now: deps.now(),
16486
+ signals: deps.signals
16487
+ };
16488
+ return recallGraph(deps.index.allRecords(), queryEmbedding, query, {
16489
+ maxDepth: maxDepth ?? DEFAULT_TRAVERSAL_DEPTH,
16490
+ includeShared: policy.includeShared
16491
+ }, recallDeps);
16492
+ };
16493
+ }
16494
+
16495
+ // src/mcp/teamMemory.ts
16496
+ function buildTeamMemory(events) {
16497
+ const aggregate = aggregateEvents(events);
16498
+ return {
16499
+ sessions: aggregate.sessions,
16500
+ totals: aggregate.totals,
15144
16501
  decisions: aggregate.decisions,
15145
16502
  meetings: aggregate.meetings
15146
16503
  };
@@ -15367,585 +16724,224 @@ function projectRecall(results) {
15367
16724
  if (record.kind === "preference") {
15368
16725
  return {
15369
16726
  ...base,
15370
- category: record.category,
15371
- preference: record.preference
15372
- };
15373
- }
15374
- const entity = record;
15375
- return { ...base, canonicalName: entity.canonicalName, type: entity.type };
15376
- });
15377
- }
15378
- function projectGraphRecall(graph) {
15379
- return {
15380
- recall: projectRecall(graph.recall),
15381
- traversals: graph.traversals.map((entry) => ({
15382
- seedRecordId: entry.seedRecordId,
15383
- traversal: projectRelationGraph(entry.traversal)
15384
- }))
15385
- };
15386
- }
15387
- async function callSemanticRecall(recall2, kinds, args) {
15388
- const query = optionalString(args.query);
15389
- if (query === undefined) {
15390
- return textContent({ error: "requiere 'query'" });
15391
- }
15392
- const limit = optionalLimit(args.limit) ?? DEFAULT_RECALL_LIMIT;
15393
- const results = await recall2({ queryText: query, kinds, limit });
15394
- return textContent(projectRecall(results));
15395
- }
15396
- async function callRelationTraversal(traverse, args) {
15397
- const entity = optionalString(args.entity);
15398
- if (entity === undefined) {
15399
- return textContent({ error: "requiere 'entity'" });
15400
- }
15401
- const depth = optionalLimit(args.depth) ?? DEFAULT_TRAVERSAL_DEPTH2;
15402
- const graph = await traverse({ seed: entity, maxDepth: depth });
15403
- return textContent(projectRelationGraph(graph));
15404
- }
15405
- async function callGraphRecall(recall2, args) {
15406
- const query = optionalString(args.query);
15407
- if (query === undefined) {
15408
- return textContent({ error: "requiere 'query'" });
15409
- }
15410
- const limit = optionalLimit(args.limit) ?? DEFAULT_RECALL_LIMIT;
15411
- const depth = optionalLimit(args.depth) ?? DEFAULT_TRAVERSAL_DEPTH2;
15412
- const graph = await recall2({ queryText: query, limit, maxDepth: depth });
15413
- return textContent(projectGraphRecall(graph));
15414
- }
15415
- async function callTool(name, args, deps) {
15416
- if (deps.recallSemantic !== undefined) {
15417
- switch (name) {
15418
- case "recall_facts":
15419
- return callSemanticRecall(deps.recallSemantic, ["fact"], args);
15420
- case "recall_preferences":
15421
- return callSemanticRecall(deps.recallSemantic, ["preference"], args);
15422
- case "similar_tasks":
15423
- return callSemanticRecall(deps.recallSemantic, ["fact", "preference", "entity"], args);
15424
- default:
15425
- break;
15426
- }
15427
- }
15428
- if (deps.recallRelations !== undefined && name === "recall_relations") {
15429
- return callRelationTraversal(deps.recallRelations, args);
15430
- }
15431
- if (deps.recallGraph !== undefined && name === "recall_graph") {
15432
- return callGraphRecall(deps.recallGraph, args);
15433
- }
15434
- const memory = await deps.readMemory();
15435
- switch (name) {
15436
- case "list_decisions": {
15437
- const query = {};
15438
- const agent = optionalString(args.agent);
15439
- const tag = optionalString(args.tag);
15440
- const limit = optionalLimit(args.limit);
15441
- if (agent !== undefined) {
15442
- query.agent = agent;
15443
- }
15444
- if (tag !== undefined) {
15445
- query.tag = tag;
15446
- }
15447
- if (limit !== undefined) {
15448
- query.limit = limit;
15449
- }
15450
- return textContent(queryDecisions(memory, query));
15451
- }
15452
- case "list_meetings":
15453
- return textContent(queryMeetings(memory, { limit: optionalLimit(args.limit) ?? 0 }));
15454
- case "list_sessions":
15455
- return textContent(querySessions(memory, { limit: optionalLimit(args.limit) ?? 0 }));
15456
- case "cost_summary":
15457
- return textContent(costSummary(memory));
15458
- default:
15459
- return;
15460
- }
15461
- }
15462
- async function handleMcpMessage(message, deps) {
15463
- const isNotification = message.id === undefined;
15464
- const id = message.id ?? null;
15465
- const serverInfo = deps.serverInfo ?? DEFAULT_SERVER_INFO;
15466
- switch (message.method) {
15467
- case "initialize":
15468
- return response(id, {
15469
- protocolVersion: MCP_PROTOCOL_VERSION,
15470
- capabilities: { tools: {} },
15471
- serverInfo
15472
- });
15473
- case "tools/list":
15474
- return response(id, { tools: availableTools(deps) });
15475
- case "tools/call": {
15476
- const params = message.params ?? {};
15477
- const name = optionalString(params.name);
15478
- if (name === undefined) {
15479
- return errorResponse(id, -32602, "tools/call requiere 'name'");
15480
- }
15481
- const args = typeof params.arguments === "object" && params.arguments !== null ? params.arguments : {};
15482
- const result = await callTool(name, args, deps);
15483
- if (result === undefined) {
15484
- return errorResponse(id, -32602, `unknown tool: ${name}`);
15485
- }
15486
- return response(id, result);
15487
- }
15488
- default:
15489
- if (isNotification) {
15490
- return null;
15491
- }
15492
- return errorResponse(id, -32601, `unsupported method: ${message.method}`);
15493
- }
15494
- }
15495
-
15496
- // src/mcp/stdio.ts
15497
- function parseErrorResponse() {
15498
- return JSON.stringify({
15499
- jsonrpc: "2.0",
15500
- id: null,
15501
- error: { code: -32700, message: "Invalid JSON" }
15502
- });
15503
- }
15504
- async function runMcpStdio(deps, io) {
15505
- for await (const rawLine of io.lines) {
15506
- const line = rawLine.trim();
15507
- if (line.length === 0) {
15508
- continue;
15509
- }
15510
- let message;
15511
- try {
15512
- message = JSON.parse(line);
15513
- } catch {
15514
- io.write(`${parseErrorResponse()}
15515
- `);
15516
- continue;
15517
- }
15518
- const reply = await handleMcpMessage(message, deps);
15519
- if (reply !== null) {
15520
- io.write(`${JSON.stringify(reply)}
15521
- `);
15522
- }
15523
- }
15524
- }
15525
- async function* linesFromReadable(readable) {
15526
- let buffer = "";
15527
- for await (const chunk of readable) {
15528
- buffer += typeof chunk === "string" ? chunk : chunk.toString("utf8");
15529
- let newlineIndex = buffer.indexOf(`
15530
- `);
15531
- while (newlineIndex >= 0) {
15532
- yield buffer.slice(0, newlineIndex);
15533
- buffer = buffer.slice(newlineIndex + 1);
15534
- newlineIndex = buffer.indexOf(`
15535
- `);
15536
- }
15537
- }
15538
- if (buffer.length > 0) {
15539
- yield buffer;
15540
- }
15541
- }
15542
-
15543
- // src/orchestrator/rosterPersistence.ts
15544
- function rosterFence(roster) {
15545
- const data = { universe: roster.universe, entries: roster.entries };
15546
- return `\`\`\`json
15547
- ${JSON.stringify(data, null, 2)}
15548
- \`\`\``;
15549
- }
15550
- function serializeRoster(roster) {
15551
- return `# openteam roster
15552
-
15553
- Universe: ${roster.universe}
15554
-
15555
- ${rosterFence(roster)}
15556
- `;
15557
- }
15558
- async function persistRoster(storage, roster, path4 = OPENTEAM_ROSTER_PATH) {
15559
- await storage.write(path4, serializeRoster(roster));
15560
- return path4;
15561
- }
15562
- async function loadRosterFile(storage, path4 = OPENTEAM_ROSTER_PATH) {
15563
- const content = await storage.read(path4);
15564
- if (content === undefined) {
15565
- return { status: "absent", path: path4 };
15566
- }
15567
- const parsed = parseRosterResult(content);
15568
- if (!parsed.ok) {
15569
- return { status: "invalid", path: path4, error: parsed.error };
15570
- }
15571
- return {
15572
- status: "loaded",
15573
- path: path4,
15574
- roster: enforceGuaranteedRoles(parsed.roster)
15575
- };
15576
- }
15577
- async function loadOrGenerateRoster(storage, generate, path4 = OPENTEAM_ROSTER_PATH) {
15578
- const existing = await loadRosterFile(storage, path4);
15579
- if (existing.status === "loaded") {
15580
- return { roster: existing.roster, regenerated: false };
15581
- }
15582
- const roster = enforceGuaranteedRoles(await generate());
15583
- if (existing.status === "absent") {
15584
- await persistRoster(storage, roster, path4);
15585
- return { roster, regenerated: true };
15586
- }
15587
- return {
15588
- roster,
15589
- regenerated: true,
15590
- recoveredFromInvalidPersisted: true,
15591
- invalidPersistedRosterError: existing.error.message
15592
- };
16727
+ category: record.category,
16728
+ preference: record.preference
16729
+ };
16730
+ }
16731
+ const entity = record;
16732
+ return { ...base, canonicalName: entity.canonicalName, type: entity.type };
16733
+ });
15593
16734
  }
15594
-
15595
- // src/plugin/orchestrateTool.ts
15596
- import { tool } from "@opencode-ai/plugin";
15597
- import { z as z20 } from "zod";
15598
- var _pluginWarnedRoles = new Set;
15599
- var RoleAssignmentSchema = z20.object({
15600
- roleID: z20.string().min(1).describe("Role identifier from the roster"),
15601
- prompt: z20.string().min(1).describe("Work prompt for this role"),
15602
- title: z20.string().min(1).optional().describe("Human-readable title"),
15603
- dependsOn: z20.array(z20.string().min(1)).optional().describe("Role IDs this assignment depends on (DAG edges)")
15604
- }).strict();
15605
- var OrchestratePayloadSchema = z20.object({
15606
- assignments: z20.array(RoleAssignmentSchema).min(1).describe("Role assignments to distribute"),
15607
- parentSessionID: z20.string().min(1).optional().describe("Parent session for subsessions"),
15608
- directory: z20.string().min(1).optional().describe("Working directory override")
15609
- }).strict();
15610
- function adaptSdkClient(sdk) {
16735
+ function projectGraphRecall(graph) {
15611
16736
  return {
15612
- session: {
15613
- create: async (args) => {
15614
- const result = await sdk.session.create(args);
15615
- const out = {};
15616
- if (result.data !== undefined) {
15617
- out.data = result.data;
15618
- }
15619
- if (result.error !== undefined) {
15620
- out.error = result.error;
15621
- }
15622
- return out;
15623
- },
15624
- prompt: async (args) => {
15625
- const result = await sdk.session.prompt(args);
15626
- const out = {};
15627
- if (result.data !== undefined) {
15628
- out.data = result.data;
15629
- }
15630
- if (result.error !== undefined) {
15631
- out.error = result.error;
15632
- }
15633
- return out;
15634
- }
15635
- }
16737
+ recall: projectRecall(graph.recall),
16738
+ traversals: graph.traversals.map((entry) => ({
16739
+ seedRecordId: entry.seedRecordId,
16740
+ traversal: projectRelationGraph(entry.traversal)
16741
+ }))
15636
16742
  };
15637
16743
  }
15638
-
15639
- // src/memory/consolidate.ts
15640
- var DAY_MS2 = 86400000;
15641
- var DEFAULT_DECAY_PARAMS = {
15642
- halfLifeMs: 90 * DAY_MS2,
15643
- forgetThreshold: 0.05
15644
- };
15645
- function lastReinforcedAt(record) {
15646
- if (record.kind === "preference" && record.lastAccessedAt !== null) {
15647
- return record.lastAccessedAt;
16744
+ async function callSemanticRecall(recall2, kinds, args) {
16745
+ const query = optionalString(args.query);
16746
+ if (query === undefined) {
16747
+ return textContent({ error: "requiere 'query'" });
15648
16748
  }
15649
- return record.createdAt;
15650
- }
15651
- function decayedConfidence(record, now, params = DEFAULT_DECAY_PARAMS) {
15652
- const ageMs = Math.max(0, now - lastReinforcedAt(record));
15653
- return record.confidence * 0.5 ** (ageMs / params.halfLifeMs);
16749
+ const limit = optionalLimit(args.limit) ?? DEFAULT_RECALL_LIMIT;
16750
+ const results = await recall2({ queryText: query, kinds, limit });
16751
+ return textContent(projectRecall(results));
15654
16752
  }
15655
- function consolidate(records, now, params = DEFAULT_DECAY_PARAMS) {
15656
- const tombstones = [];
15657
- for (const record of records) {
15658
- if (!isTransactionActiveAt(record, now)) {
15659
- continue;
15660
- }
15661
- if (decayedConfidence(record, now, params) < params.forgetThreshold) {
15662
- tombstones.push({ ...record, invalidatedAt: now, supersededBy: null });
15663
- }
16753
+ async function callRelationTraversal(traverse, args) {
16754
+ const entity = optionalString(args.entity);
16755
+ if (entity === undefined) {
16756
+ return textContent({ error: "requiere 'entity'" });
15664
16757
  }
15665
- return tombstones;
16758
+ const depth = optionalLimit(args.depth) ?? DEFAULT_TRAVERSAL_DEPTH2;
16759
+ const graph = await traverse({ seed: entity, maxDepth: depth });
16760
+ return textContent(projectRelationGraph(graph));
15666
16761
  }
15667
-
15668
- // src/storage/index/memoryConsolidate.ts
15669
- async function runConsolidation(logPath, deps) {
15670
- const params = deps.params ?? DEFAULT_DECAY_PARAMS;
15671
- const text2 = await deps.storage.read(logPath) ?? "";
15672
- const { records, rejectedLines } = parseMemoryLog(text2);
15673
- const state = foldAssertions(records);
15674
- const tombstones = consolidate(allRecords(state), deps.now, params);
15675
- if (tombstones.length === 0) {
15676
- return { forgotten: 0, rejectedLines };
16762
+ async function callGraphRecall(recall2, args) {
16763
+ const query = optionalString(args.query);
16764
+ if (query === undefined) {
16765
+ return textContent({ error: "requiere 'query'" });
15677
16766
  }
15678
- const appended = `${tombstones.map(serializeAssertion).join(`
15679
- `)}
15680
- `;
15681
- await deps.storage.append(logPath, appended);
15682
- return { forgotten: tombstones.length, rejectedLines };
16767
+ const limit = optionalLimit(args.limit) ?? DEFAULT_RECALL_LIMIT;
16768
+ const depth = optionalLimit(args.depth) ?? DEFAULT_TRAVERSAL_DEPTH2;
16769
+ const graph = await recall2({ queryText: query, limit, maxDepth: depth });
16770
+ return textContent(projectGraphRecall(graph));
15683
16771
  }
15684
-
15685
- // src/local/embeddings.ts
15686
- function parseEmbeddingsResponse(payload) {
15687
- if (typeof payload !== "object" || payload === null || !Array.isArray(payload.data)) {
15688
- throw new Error("Malformed /embeddings response: missing 'data'");
15689
- }
15690
- const data = payload.data;
15691
- return data.map((entry, index) => {
15692
- const embedding = typeof entry === "object" && entry !== null ? entry.embedding : undefined;
15693
- if (!Array.isArray(embedding) || !embedding.every((n) => typeof n === "number" && Number.isFinite(n))) {
15694
- throw new Error(`Malformed /embeddings response: invalid vector at index ${index}`);
16772
+ async function callTool(name, args, deps) {
16773
+ if (deps.recallSemantic !== undefined) {
16774
+ switch (name) {
16775
+ case "recall_facts":
16776
+ return callSemanticRecall(deps.recallSemantic, ["fact"], args);
16777
+ case "recall_preferences":
16778
+ return callSemanticRecall(deps.recallSemantic, ["preference"], args);
16779
+ case "similar_tasks":
16780
+ return callSemanticRecall(deps.recallSemantic, ["fact", "preference", "entity"], args);
16781
+ default:
16782
+ break;
15695
16783
  }
15696
- return embedding;
15697
- });
15698
- }
15699
- async function embedTexts(request, deps) {
15700
- if (request.texts.length === 0) {
15701
- return [];
15702
- }
15703
- let response2;
15704
- try {
15705
- response2 = await deps.fetch(embeddingsEndpoint(request.baseURL), {
15706
- method: "POST",
15707
- headers: {
15708
- "content-type": "application/json",
15709
- accept: "application/json"
15710
- },
15711
- body: JSON.stringify({
15712
- model: request.modelID,
15713
- input: [...request.texts]
15714
- })
15715
- });
15716
- } catch (error) {
15717
- throw new Error(`Local embeddings failed: ${errorMessage(error)}`);
15718
- }
15719
- if (!response2.ok) {
15720
- throw new Error(`Local embeddings failed: HTTP ${response2.status} on /embeddings`);
15721
- }
15722
- let payload;
15723
- try {
15724
- payload = await response2.json();
15725
- } catch (error) {
15726
- throw new Error(`Local embeddings failed: ${errorMessage(error)}`);
15727
- }
15728
- const vectors = parseEmbeddingsResponse(payload);
15729
- if (vectors.length !== request.texts.length) {
15730
- throw new Error(`Local embeddings returned ${vectors.length} vectors for ${request.texts.length} texts`);
15731
16784
  }
15732
- return vectors;
15733
- }
15734
- function createLocalEmbedder(request, deps) {
15735
- return (texts) => embedTexts({ ...request, texts }, deps);
15736
- }
15737
-
15738
- // src/memory/embedText.ts
15739
- function normalize(parts) {
15740
- return parts.map((part) => part.trim()).filter((part) => part.length > 0).join(" ");
15741
- }
15742
- function recordEmbeddingText(record) {
15743
- if (record.kind === "fact") {
15744
- return normalize([
15745
- record.subject,
15746
- record.predicate,
15747
- record.object,
15748
- record.category ?? ""
15749
- ]);
16785
+ if (deps.recallRelations !== undefined && name === "recall_relations") {
16786
+ return callRelationTraversal(deps.recallRelations, args);
15750
16787
  }
15751
- if (record.kind === "preference") {
15752
- return normalize([
15753
- record.category,
15754
- record.preference,
15755
- record.context ?? ""
15756
- ]);
16788
+ if (deps.recallGraph !== undefined && name === "recall_graph") {
16789
+ return callGraphRecall(deps.recallGraph, args);
15757
16790
  }
15758
- if (record.kind === "relation") {
15759
- return normalize([
15760
- record.from,
15761
- record.predicate,
15762
- record.to,
15763
- record.annotation ?? ""
15764
- ]);
16791
+ const memory = await deps.readMemory();
16792
+ switch (name) {
16793
+ case "list_decisions": {
16794
+ const query = {};
16795
+ const agent = optionalString(args.agent);
16796
+ const tag = optionalString(args.tag);
16797
+ const limit = optionalLimit(args.limit);
16798
+ if (agent !== undefined) {
16799
+ query.agent = agent;
16800
+ }
16801
+ if (tag !== undefined) {
16802
+ query.tag = tag;
16803
+ }
16804
+ if (limit !== undefined) {
16805
+ query.limit = limit;
16806
+ }
16807
+ return textContent(queryDecisions(memory, query));
16808
+ }
16809
+ case "list_meetings":
16810
+ return textContent(queryMeetings(memory, { limit: optionalLimit(args.limit) ?? 0 }));
16811
+ case "list_sessions":
16812
+ return textContent(querySessions(memory, { limit: optionalLimit(args.limit) ?? 0 }));
16813
+ case "cost_summary":
16814
+ return textContent(costSummary(memory));
16815
+ default:
16816
+ return;
15765
16817
  }
15766
- return normalize([record.canonicalName, record.type, ...record.aliases]);
15767
16818
  }
15768
-
15769
- // src/storage/index/memoryEmbeddings.ts
15770
- async function ensureMemoryEmbeddings(deps) {
15771
- const { index, embedder, model } = deps;
15772
- const warn = deps.warn ?? console.warn;
15773
- const storedModel = index.embeddingModel();
15774
- const modelChanged = storedModel !== null && storedModel !== model;
15775
- if (modelChanged) {
15776
- index.clearEmbeddings();
16819
+ async function handleMcpMessage(message, deps) {
16820
+ const isNotification = message.id === undefined;
16821
+ const id = message.id ?? null;
16822
+ const serverInfo = deps.serverInfo ?? DEFAULT_SERVER_INFO;
16823
+ switch (message.method) {
16824
+ case "initialize":
16825
+ return response(id, {
16826
+ protocolVersion: MCP_PROTOCOL_VERSION,
16827
+ capabilities: { tools: {} },
16828
+ serverInfo
16829
+ });
16830
+ case "tools/list":
16831
+ return response(id, { tools: availableTools(deps) });
16832
+ case "tools/call": {
16833
+ const params = message.params ?? {};
16834
+ const name = optionalString(params.name);
16835
+ if (name === undefined) {
16836
+ return errorResponse(id, -32602, "tools/call requiere 'name'");
16837
+ }
16838
+ const args = typeof params.arguments === "object" && params.arguments !== null ? params.arguments : {};
16839
+ const result = await callTool(name, args, deps);
16840
+ if (result === undefined) {
16841
+ return errorResponse(id, -32602, `unknown tool: ${name}`);
16842
+ }
16843
+ return response(id, result);
16844
+ }
16845
+ default:
16846
+ if (isNotification) {
16847
+ return null;
16848
+ }
16849
+ return errorResponse(id, -32601, `unsupported method: ${message.method}`);
15777
16850
  }
15778
- index.setEmbeddingModel(model);
15779
- const records = index.allRecords();
15780
- index.pruneEmbeddings();
15781
- const have = modelChanged ? new Set : new Set(index.embeddingMap().keys());
15782
- const todo = records.filter((record) => !have.has(record.id));
15783
- if (todo.length === 0) {
15784
- return { embedded: 0, failed: 0, modelChanged };
16851
+ }
16852
+
16853
+ // src/mcp/stdio.ts
16854
+ function parseErrorResponse() {
16855
+ return JSON.stringify({
16856
+ jsonrpc: "2.0",
16857
+ id: null,
16858
+ error: { code: -32700, message: "Invalid JSON" }
16859
+ });
16860
+ }
16861
+ async function runMcpStdio(deps, io) {
16862
+ for await (const rawLine of io.lines) {
16863
+ const line = rawLine.trim();
16864
+ if (line.length === 0) {
16865
+ continue;
16866
+ }
16867
+ let message;
16868
+ try {
16869
+ message = JSON.parse(line);
16870
+ } catch {
16871
+ io.write(`${parseErrorResponse()}
16872
+ `);
16873
+ continue;
16874
+ }
16875
+ const reply = await handleMcpMessage(message, deps);
16876
+ if (reply !== null) {
16877
+ io.write(`${JSON.stringify(reply)}
16878
+ `);
16879
+ }
15785
16880
  }
15786
- let vectors;
15787
- try {
15788
- vectors = await embedder(todo.map(recordEmbeddingText));
15789
- } catch (error) {
15790
- const reason = error instanceof Error ? error.message : String(error);
15791
- warn(`[openteam] could not compute embeddings for ${todo.length} memory record(s): ${reason}. ` + "Recall over them is unavailable until the local runtime is reachable.");
15792
- return { embedded: 0, failed: todo.length, modelChanged };
16881
+ }
16882
+ async function* linesFromReadable(readable) {
16883
+ let buffer = "";
16884
+ for await (const chunk of readable) {
16885
+ buffer += typeof chunk === "string" ? chunk : chunk.toString("utf8");
16886
+ let newlineIndex = buffer.indexOf(`
16887
+ `);
16888
+ while (newlineIndex >= 0) {
16889
+ yield buffer.slice(0, newlineIndex);
16890
+ buffer = buffer.slice(newlineIndex + 1);
16891
+ newlineIndex = buffer.indexOf(`
16892
+ `);
16893
+ }
15793
16894
  }
15794
- if (vectors.length !== todo.length) {
15795
- warn(`[openteam] could not compute embeddings for ${todo.length} memory record(s): ` + `the embedder returned ${vectors.length} vector(s). Recall over them is unavailable.`);
15796
- return { embedded: 0, failed: todo.length, modelChanged };
16895
+ if (buffer.length > 0) {
16896
+ yield buffer;
15797
16897
  }
15798
- const entries = todo.map((record, position) => ({
15799
- recordId: record.id,
15800
- vector: vectors[position]
15801
- }));
15802
- index.putEmbeddings(entries);
15803
- return { embedded: entries.length, failed: 0, modelChanged };
15804
16898
  }
15805
16899
 
15806
- // src/storage/index/memoryRuntime.ts
15807
- async function buildMemoryRuntimeFromConfig(config, deps, options = {}) {
15808
- const semantic = resolveMemorySemantic(config);
15809
- if (!semantic.enabled || semantic.embeddings.provider === "none") {
15810
- return;
15811
- }
15812
- if (options.requireInjectionEnabled === true && !semantic.injection.enabled) {
15813
- return;
15814
- }
15815
- const runtime = config.local.runtimes.find((rt) => rt.enabled && rt.baseURL !== undefined);
15816
- if (runtime?.baseURL === undefined) {
15817
- return;
16900
+ // src/memory/consolidate.ts
16901
+ var DAY_MS2 = 86400000;
16902
+ var DEFAULT_DECAY_PARAMS = {
16903
+ halfLifeMs: 90 * DAY_MS2,
16904
+ forgetThreshold: 0.05
16905
+ };
16906
+ function lastReinforcedAt(record) {
16907
+ if (record.kind === "preference" && record.lastAccessedAt !== null) {
16908
+ return record.lastAccessedAt;
15818
16909
  }
15819
- const modelID = semantic.embeddings.model?.modelID ?? runtime.defaultModel.modelID;
15820
- const openDatabase = deps.openDatabase ?? createBunSqliteDatabase;
15821
- const makeEmbedder = deps.createEmbedder ?? createLocalEmbedder;
15822
- const fetchImpl = deps.fetch ?? globalThis.fetch;
15823
- const now = deps.now ?? (() => Date.now());
15824
- const warn = deps.warn ?? console.warn;
15825
- try {
15826
- const db = await openDatabase(semantic.indexPath);
15827
- const index = createMemoryIndex(db, warn);
15828
- const rebuildResult = await rebuildMemoryIndexFromStorage(index, semantic.logPath, { storage: deps.storage });
15829
- if (rebuildResult.rejectedLines > 0) {
15830
- warn(`[openteam] memory log rejected ${rebuildResult.rejectedLines} line(s); semantic memory may be incomplete.`);
16910
+ return record.createdAt;
16911
+ }
16912
+ function decayedConfidence(record, now, params = DEFAULT_DECAY_PARAMS) {
16913
+ const ageMs = Math.max(0, now - lastReinforcedAt(record));
16914
+ return record.confidence * 0.5 ** (ageMs / params.halfLifeMs);
16915
+ }
16916
+ function consolidate(records, now, params = DEFAULT_DECAY_PARAMS) {
16917
+ const tombstones = [];
16918
+ for (const record of records) {
16919
+ if (!isTransactionActiveAt(record, now)) {
16920
+ continue;
16921
+ }
16922
+ if (decayedConfidence(record, now, params) < params.forgetThreshold) {
16923
+ tombstones.push({ ...record, invalidatedAt: now, supersededBy: null });
15831
16924
  }
15832
- const embedder = makeEmbedder({ baseURL: runtime.baseURL, modelID }, { fetch: fetchImpl });
15833
- await ensureMemoryEmbeddings({ index, embedder, model: modelID, warn });
15834
- return {
15835
- index,
15836
- embedder,
15837
- now,
15838
- ownerKey: semantic.scope === "user" ? "user" : "project",
15839
- semantic
15840
- };
15841
- } catch {
15842
- return;
15843
16925
  }
16926
+ return tombstones;
15844
16927
  }
15845
- // package.json
15846
- var package_default = {
15847
- name: "@jmanuelcorral/openteam",
15848
- version: "0.16.0",
15849
- packageManager: "bun@1.3.14",
15850
- description: "Cost-aware, local-first routing plugin for opencode with cheapest-capable frontier fallback and multi-agent orchestration.",
15851
- license: "MIT",
15852
- author: "Jose Manuel Corral",
15853
- repository: {
15854
- type: "git",
15855
- url: "git+https://github.com/jmanuelcorral/openteam.git"
15856
- },
15857
- homepage: "https://github.com/jmanuelcorral/openteam#readme",
15858
- bugs: {
15859
- url: "https://github.com/jmanuelcorral/openteam/issues"
15860
- },
15861
- keywords: [
15862
- "opencode",
15863
- "opencode-plugin",
15864
- "llm",
15865
- "routing",
15866
- "local-llm",
15867
- "ollama",
15868
- "lm-studio",
15869
- "foundry-local",
15870
- "cost-optimization",
15871
- "multi-agent"
15872
- ],
15873
- engines: {
15874
- bun: ">=1.3",
15875
- node: "^22.22.2 || ^24.15.0 || >=26.0.0"
15876
- },
15877
- type: "module",
15878
- main: "./dist/index.js",
15879
- module: "./dist/index.js",
15880
- types: "./dist/index.d.ts",
15881
- bin: {
15882
- openteam: "./dist/cli.js"
15883
- },
15884
- exports: {
15885
- ".": {
15886
- types: "./dist/index.d.ts",
15887
- import: "./dist/index.js"
15888
- },
15889
- "./package.json": "./package.json"
15890
- },
15891
- files: [
15892
- "dist",
15893
- "README.md",
15894
- "LICENSE",
15895
- "AGENTS.md",
15896
- ".opencode/openteam.example.json",
15897
- ".opencode/command/openteam.md"
15898
- ],
15899
- publishConfig: {
15900
- access: "public",
15901
- registry: "https://registry.npmjs.org/"
15902
- },
15903
- trustedDependencies: [],
15904
- sideEffects: false,
15905
- scripts: {
15906
- prebuild: "bun run clean",
15907
- build: "bun run build:js && bun run build:cli && bun run build:types",
15908
- "build:js": "bun build ./src/index.ts --target=node --format=esm --outfile=dist/index.js --external @opencode-ai/plugin --external @opencode-ai/sdk --external zod --external @opentelemetry/api --external @opentelemetry/sdk-trace-base --external @opentelemetry/exporter-trace-otlp-http --external @opentelemetry/resources --external @langchain/langgraph",
15909
- "build:cli": 'bun build ./src/cli.ts --target=node --format=esm --outfile=dist/cli.js --banner "#!/usr/bin/env node" --external @opencode-ai/plugin --external @opencode-ai/sdk --external zod --external @clack/prompts --external @opentelemetry/api --external @opentelemetry/sdk-trace-base --external @opentelemetry/exporter-trace-otlp-http --external @opentelemetry/resources --external @langchain/langgraph',
15910
- "build:types": "tsc -p tsconfig.build.json",
15911
- clean: `node -e "require('node:fs').rmSync('dist', { recursive: true, force: true })"`,
15912
- test: "bun test",
15913
- "test:cov": "bun test --coverage",
15914
- "certify:shadow": "bun run scripts/certify-artifact.ts shadow",
15915
- "certify:release": "bun run scripts/certify-artifact.ts release",
15916
- "coverage:check": "node scripts/check-coverage.mjs",
15917
- typecheck: "tsc --noEmit",
15918
- lint: "biome check .",
15919
- "format:check": "biome format .",
15920
- "docs:install": "cd docs && bun install --frozen-lockfile --ignore-scripts",
15921
- "docs:dev": "cd docs && bun run docs:dev",
15922
- "docs:build": "cd docs && bun run docs:build",
15923
- "docs:preview": "cd docs && bun run docs:preview",
15924
- prepublishOnly: "bun run build",
15925
- "link:local": "bun run build && npm link",
15926
- "hooks:install": "git config core.hooksPath .githooks"
15927
- },
15928
- dependencies: {
15929
- "@clack/prompts": "1.7.0",
15930
- "@langchain/core": "1.2.9",
15931
- "@langchain/langgraph": "1.4.12",
15932
- "@opencode-ai/plugin": "1.18.19",
15933
- "@opencode-ai/sdk": "1.18.19",
15934
- "@opentelemetry/api": "1.9.1",
15935
- "@opentelemetry/exporter-trace-otlp-http": "0.221.0",
15936
- "@opentelemetry/resources": "2.10.0",
15937
- "@opentelemetry/sdk-trace-base": "2.10.0",
15938
- zod: "4.4.3"
15939
- },
15940
- devDependencies: {
15941
- "@biomejs/biome": "2.5.9",
15942
- "@types/bun": "1.3.14",
15943
- typescript: "7.0.2"
15944
- }
15945
- };
15946
16928
 
15947
- // src/version.ts
15948
- var PACKAGE_VERSION = package_default.version;
16929
+ // src/storage/index/memoryConsolidate.ts
16930
+ async function runConsolidation(logPath, deps) {
16931
+ const params = deps.params ?? DEFAULT_DECAY_PARAMS;
16932
+ const text2 = await deps.storage.read(logPath) ?? "";
16933
+ const { records, rejectedLines } = parseMemoryLog(text2);
16934
+ const state = foldAssertions(records);
16935
+ const tombstones = consolidate(allRecords(state), deps.now, params);
16936
+ if (tombstones.length === 0) {
16937
+ return { forgotten: 0, rejectedLines };
16938
+ }
16939
+ const appended = `${tombstones.map(serializeAssertion).join(`
16940
+ `)}
16941
+ `;
16942
+ await deps.storage.append(logPath, appended);
16943
+ return { forgotten: tombstones.length, rejectedLines };
16944
+ }
15949
16945
 
15950
16946
  // src/web/git.ts
15951
16947
  function createGitLastCommit(exec) {
@@ -15968,7 +16964,8 @@ function createGitLastCommit(exec) {
15968
16964
 
15969
16965
  // src/cli.ts
15970
16966
  var execFileAsync2 = promisify2(execFile2);
15971
- var AGENT_DIR2 = dirname5(ORCHESTRATOR_AGENT_PATH);
16967
+ var execAsync = promisify2(exec);
16968
+ var AGENT_DIR2 = dirname6(ORCHESTRATOR_AGENT_PATH);
15972
16969
  var storage = createFsStorageProvider(process.cwd());
15973
16970
  async function readStoredRosterForDoctor() {
15974
16971
  const content = await storage.read(OPENTEAM_ROSTER_PATH);
@@ -15996,6 +16993,22 @@ var nodeExec2 = async (command, args) => {
15996
16993
  };
15997
16994
  }
15998
16995
  };
16996
+ var requireFromCli = createRequire2(import.meta.url);
16997
+ function readPackageVersion(packageName) {
16998
+ try {
16999
+ const pkg = requireFromCli(`${packageName}/package.json`);
17000
+ return typeof pkg.version === "string" ? pkg.version : undefined;
17001
+ } catch {
17002
+ return;
17003
+ }
17004
+ }
17005
+ async function runOpencodeVersionCommand() {
17006
+ const { stdout } = await execAsync("opencode --version", {
17007
+ timeout: 4000,
17008
+ windowsHide: true
17009
+ });
17010
+ return stdout;
17011
+ }
15999
17012
  function describeFileReadError(error) {
16000
17013
  const code = typeof error === "object" && error !== null && "code" in error ? String(error.code) : "";
16001
17014
  if (code === "ENOENT") {
@@ -16011,7 +17024,7 @@ async function readConfigFile(path4, resolution) {
16011
17024
  return storage.read(path4);
16012
17025
  }
16013
17026
  try {
16014
- return await readFile2(resolve6(path4), "utf8");
17027
+ return await readFile3(resolve6(path4), "utf8");
16015
17028
  } catch (error) {
16016
17029
  throw new Error(`[openteam] config file named by --config could not be read: ${path4} (${describeFileReadError(error)})`);
16017
17030
  }
@@ -16030,7 +17043,7 @@ async function saveConfig(config, path4, resolution = { explicit: false }) {
16030
17043
  return;
16031
17044
  }
16032
17045
  const diskPath = resolve6(path4);
16033
- await mkdir2(dirname5(diskPath), { recursive: true });
17046
+ await mkdir2(dirname6(diskPath), { recursive: true });
16034
17047
  await writeFile2(diskPath, `${JSON.stringify(config, null, 2)}
16035
17048
  `, "utf8");
16036
17049
  }
@@ -16153,7 +17166,11 @@ var deps = {
16153
17166
  storage,
16154
17167
  holder: "openteam-cli",
16155
17168
  exec: nodeExec2,
16156
- repoRoot: process.cwd()
17169
+ repoRoot: process.cwd(),
17170
+ evaluateCutoverGate: async (gateConfig) => evaluateCutoverGateForBoundary(gateConfig, storage, await resolveOpencodeVersionBestEffort({
17171
+ readPackageVersion,
17172
+ runVersionCommand: runOpencodeVersionCommand
17173
+ }))
16157
17174
  }),
16158
17175
  configPath: DEFAULT_CONFIG_PATH,
16159
17176
  telemetryPath: DEFAULT_TELEMETRY_PATH,