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