@jmanuelcorral/openteam 0.13.0 → 0.14.0
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 +4 -2
- package/README.md +4 -2
- package/dist/cli.js +275 -30
- package/dist/commands/dispatch.d.ts +15 -0
- package/dist/commands/dispatch.d.ts.map +1 -1
- package/dist/commands/doctor.d.ts +30 -0
- package/dist/commands/doctor.d.ts.map +1 -1
- package/dist/commands/purge.d.ts.map +1 -1
- package/dist/commands/setup.d.ts +19 -1
- package/dist/commands/setup.d.ts.map +1 -1
- package/dist/config/errors.d.ts.map +1 -1
- package/dist/config/schema.d.ts +23 -6
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +392 -162
- package/dist/local/concurrency.d.ts +24 -4
- package/dist/local/concurrency.d.ts.map +1 -1
- package/dist/local/registry.d.ts +3 -3
- package/dist/local/registry.d.ts.map +1 -1
- package/dist/local/types.d.ts +16 -2
- package/dist/local/types.d.ts.map +1 -1
- package/dist/orchestrator/coordinator.d.ts +24 -1
- package/dist/orchestrator/coordinator.d.ts.map +1 -1
- package/dist/orchestrator/roles.d.ts +2 -12
- package/dist/orchestrator/roles.d.ts.map +1 -1
- package/dist/storage/graph/soakRecorder.d.ts +19 -0
- package/dist/storage/graph/soakRecorder.d.ts.map +1 -1
- package/dist/telemetry/eventLog.d.ts +7 -1
- package/dist/telemetry/eventLog.d.ts.map +1 -1
- package/dist/telemetry/events.d.ts +37 -0
- package/dist/telemetry/events.d.ts.map +1 -1
- package/dist/telemetry/queueWait.d.ts +51 -0
- package/dist/telemetry/queueWait.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -95,8 +95,8 @@ var AgentRoleProfileSchema = z.object({
|
|
|
95
95
|
preferredFrontierModels: z.array(z.union([modelSelectionSchema, z.literal("auto")])),
|
|
96
96
|
localFirst: z.boolean(),
|
|
97
97
|
requiresLocalRuntime: z.boolean().optional(),
|
|
98
|
-
localRuntimes: z.array(z.
|
|
99
|
-
localModels: z.
|
|
98
|
+
localRuntimes: z.array(z.string().min(1)).optional(),
|
|
99
|
+
localModels: z.record(z.string().min(1), z.string().min(1)).optional()
|
|
100
100
|
}).strict();
|
|
101
101
|
var TEAM_ROLES = {
|
|
102
102
|
architect: {
|
|
@@ -403,14 +403,33 @@ var defaultFrontierModel = {
|
|
|
403
403
|
modelID: "claude-sonnet-4-5"
|
|
404
404
|
};
|
|
405
405
|
var DEFAULT_LOCAL_MAX_CONCURRENCY = 4;
|
|
406
|
+
var LocalRuntimeKindSchema = z2.enum([
|
|
407
|
+
"ollama",
|
|
408
|
+
"lmstudio",
|
|
409
|
+
"foundry-local",
|
|
410
|
+
"lemonade"
|
|
411
|
+
]);
|
|
406
412
|
var LocalRuntimeSchema = z2.object({
|
|
407
|
-
id: z2.
|
|
413
|
+
id: z2.string().min(1),
|
|
414
|
+
kind: LocalRuntimeKindSchema.optional(),
|
|
408
415
|
enabled: z2.boolean().default(true),
|
|
409
416
|
baseURL: z2.string().url().optional(),
|
|
410
417
|
discovery: z2.enum(["cli", "sdk", "manual"]).optional(),
|
|
411
418
|
defaultModel: ModelRefSchema,
|
|
412
419
|
maxConcurrency: z2.number().int().positive().optional()
|
|
413
|
-
}).strict()
|
|
420
|
+
}).strict().superRefine((runtime, ctx) => {
|
|
421
|
+
const resolvedKind = runtime.kind ?? runtime.id;
|
|
422
|
+
if (!LocalRuntimeKindSchema.safeParse(resolvedKind).success) {
|
|
423
|
+
ctx.addIssue({
|
|
424
|
+
code: z2.ZodIssueCode.custom,
|
|
425
|
+
path: ["kind"],
|
|
426
|
+
message: `local runtime "${runtime.id}" has no resolvable kind: add "kind" ` + "(one of ollama, lmstudio, foundry-local, lemonade), since the id is " + "not itself a runtime kind."
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
});
|
|
430
|
+
function localRuntimeKind(runtime) {
|
|
431
|
+
return runtime.kind ?? runtime.id;
|
|
432
|
+
}
|
|
414
433
|
var MemoryScopeSchema = z2.enum(["project", "user"]);
|
|
415
434
|
var MemoryExtractionModeSchema = z2.enum([
|
|
416
435
|
"local",
|
|
@@ -492,7 +511,23 @@ var OpenTeamConfigObjectSchema = z2.object({
|
|
|
492
511
|
localOnly: false
|
|
493
512
|
}),
|
|
494
513
|
local: z2.object({
|
|
495
|
-
runtimes: z2.array(LocalRuntimeSchema).default([])
|
|
514
|
+
runtimes: z2.array(LocalRuntimeSchema).default([]).superRefine((runtimes, ctx) => {
|
|
515
|
+
const seen = new Set;
|
|
516
|
+
for (let index = 0;index < runtimes.length; index += 1) {
|
|
517
|
+
const runtime = runtimes[index];
|
|
518
|
+
if (runtime === undefined) {
|
|
519
|
+
continue;
|
|
520
|
+
}
|
|
521
|
+
if (seen.has(runtime.id)) {
|
|
522
|
+
ctx.addIssue({
|
|
523
|
+
code: z2.ZodIssueCode.custom,
|
|
524
|
+
path: [index, "id"],
|
|
525
|
+
message: `duplicate local runtime id "${runtime.id}": each local.runtimes[].id must be unique within the array.`
|
|
526
|
+
});
|
|
527
|
+
}
|
|
528
|
+
seen.add(runtime.id);
|
|
529
|
+
}
|
|
530
|
+
})
|
|
496
531
|
}).strict().default({
|
|
497
532
|
runtimes: []
|
|
498
533
|
}),
|
|
@@ -2681,6 +2716,7 @@ var KNOWN_CONFIG_KEYS_BY_PATH = new Map([
|
|
|
2681
2716
|
"local.runtimes[]",
|
|
2682
2717
|
[
|
|
2683
2718
|
"id",
|
|
2719
|
+
"kind",
|
|
2684
2720
|
"enabled",
|
|
2685
2721
|
"baseURL",
|
|
2686
2722
|
"discovery",
|
|
@@ -3031,6 +3067,16 @@ var MeetingEventSchema = EventBaseSchema.extend({
|
|
|
3031
3067
|
roles: z11.array(z11.string().min(1)),
|
|
3032
3068
|
decisionIDs: z11.array(z11.string().min(1)).optional()
|
|
3033
3069
|
});
|
|
3070
|
+
var QueueEventSchema = EventBaseSchema.extend({
|
|
3071
|
+
type: z11.literal("queue"),
|
|
3072
|
+
runtimeId: z11.string().min(1),
|
|
3073
|
+
waitedMs: z11.number().finite().min(0),
|
|
3074
|
+
activeCount: z11.number().int().min(0),
|
|
3075
|
+
queuedCount: z11.number().int().min(0),
|
|
3076
|
+
decisionID: z11.string().min(1).optional(),
|
|
3077
|
+
agent: z11.string().min(1).optional(),
|
|
3078
|
+
batchID: z11.string().min(1).optional()
|
|
3079
|
+
});
|
|
3034
3080
|
var DecisionEventSchema = EventBaseSchema.extend({
|
|
3035
3081
|
type: z11.literal("decision"),
|
|
3036
3082
|
agent: z11.string().min(1).optional(),
|
|
@@ -3076,6 +3122,7 @@ var OpenTeamEventSchema = z11.discriminatedUnion("type", [
|
|
|
3076
3122
|
MessageEventSchema,
|
|
3077
3123
|
ToolcallEventSchema,
|
|
3078
3124
|
MeetingEventSchema,
|
|
3125
|
+
QueueEventSchema,
|
|
3079
3126
|
DecisionEventSchema,
|
|
3080
3127
|
ActivityEventSchema,
|
|
3081
3128
|
SessionEndpointEventSchema,
|
|
@@ -5052,11 +5099,14 @@ function createRuntimeConcurrencyLimiter(options) {
|
|
|
5052
5099
|
acquire(runtimeId) {
|
|
5053
5100
|
const immediate = tryAcquire(runtimeId);
|
|
5054
5101
|
if (immediate !== undefined) {
|
|
5055
|
-
return Promise.resolve(immediate);
|
|
5102
|
+
return Promise.resolve({ release: immediate, waitedMs: 0 });
|
|
5056
5103
|
}
|
|
5057
5104
|
const state = stateFor(runtimeId);
|
|
5105
|
+
const queuedAt = options.now();
|
|
5058
5106
|
return new Promise((resolve3) => {
|
|
5059
|
-
state.queue.push(
|
|
5107
|
+
state.queue.push((release) => {
|
|
5108
|
+
resolve3({ release, waitedMs: options.now() - queuedAt });
|
|
5109
|
+
});
|
|
5060
5110
|
});
|
|
5061
5111
|
},
|
|
5062
5112
|
activeCount(runtimeId) {
|
|
@@ -5414,10 +5464,11 @@ class RuntimeRegistry {
|
|
|
5414
5464
|
continue;
|
|
5415
5465
|
}
|
|
5416
5466
|
const probedAt = this.clock();
|
|
5417
|
-
const adapter = this.adapters[runtime
|
|
5467
|
+
const adapter = this.adapters[localRuntimeKind(runtime)];
|
|
5418
5468
|
const options = this.createProbeOptions(runtime, probedAt);
|
|
5419
5469
|
try {
|
|
5420
|
-
|
|
5470
|
+
const snapshot = await adapter.probe(options);
|
|
5471
|
+
snapshots.push({ ...snapshot, id: runtime.id });
|
|
5421
5472
|
} catch (error) {
|
|
5422
5473
|
snapshots.push(unavailableSnapshot({
|
|
5423
5474
|
id: runtime.id,
|
|
@@ -5437,7 +5488,7 @@ class RuntimeRegistry {
|
|
|
5437
5488
|
if (runtime.baseURL !== undefined) {
|
|
5438
5489
|
options.baseURL = runtime.baseURL;
|
|
5439
5490
|
}
|
|
5440
|
-
if (runtime
|
|
5491
|
+
if (localRuntimeKind(runtime) === "foundry-local" && runtime.discovery !== "manual" && this.foundryDiscovery !== undefined) {
|
|
5441
5492
|
options.discovery = this.foundryDiscovery;
|
|
5442
5493
|
}
|
|
5443
5494
|
return options;
|
|
@@ -6538,6 +6589,28 @@ async function safeEmit(sink, event) {
|
|
|
6538
6589
|
await sink.emit(event);
|
|
6539
6590
|
} catch {}
|
|
6540
6591
|
}
|
|
6592
|
+
function buildQueueEvent(observation, sessionID, ts, correlation) {
|
|
6593
|
+
const event = {
|
|
6594
|
+
v: EVENT_SCHEMA_VERSION,
|
|
6595
|
+
type: "queue",
|
|
6596
|
+
ts,
|
|
6597
|
+
sessionID,
|
|
6598
|
+
runtimeId: observation.runtimeId,
|
|
6599
|
+
waitedMs: observation.waitedMs,
|
|
6600
|
+
activeCount: observation.activeCount,
|
|
6601
|
+
queuedCount: observation.queuedCount
|
|
6602
|
+
};
|
|
6603
|
+
if (correlation.decisionID !== undefined) {
|
|
6604
|
+
event.decisionID = correlation.decisionID;
|
|
6605
|
+
}
|
|
6606
|
+
if (correlation.agent !== undefined) {
|
|
6607
|
+
event.agent = correlation.agent;
|
|
6608
|
+
}
|
|
6609
|
+
if (correlation.batchID !== undefined) {
|
|
6610
|
+
event.batchID = correlation.batchID;
|
|
6611
|
+
}
|
|
6612
|
+
return event;
|
|
6613
|
+
}
|
|
6541
6614
|
async function runRoleTaskWithCorrelation(input, deps, correlation, batch) {
|
|
6542
6615
|
if (getRoleProfile(input.roleID, input.config.orchestrator?.roles) === undefined) {
|
|
6543
6616
|
warnUnknownRole(input.roleID, deps.warn, deps.warnedRoles);
|
|
@@ -6575,16 +6648,40 @@ async function runRoleTaskWithCorrelation(input, deps, correlation, batch) {
|
|
|
6575
6648
|
const subsessionDeps = deps.timeoutMs === undefined ? {} : { timeoutMs: deps.timeoutMs };
|
|
6576
6649
|
const runtimeId = selection.runtimeId;
|
|
6577
6650
|
let release;
|
|
6651
|
+
let queueObservation;
|
|
6578
6652
|
if (runtimeId !== undefined && deps.runtimeLimiter !== undefined) {
|
|
6579
|
-
|
|
6653
|
+
const limiter = deps.runtimeLimiter;
|
|
6654
|
+
release = limiter.tryAcquire(runtimeId);
|
|
6655
|
+
let waitedMs = 0;
|
|
6580
6656
|
if (release === undefined) {
|
|
6581
|
-
|
|
6582
|
-
|
|
6657
|
+
const acquisition = await limiter.acquire(runtimeId);
|
|
6658
|
+
release = acquisition.release;
|
|
6659
|
+
waitedMs = acquisition.waitedMs;
|
|
6660
|
+
}
|
|
6661
|
+
queueObservation = {
|
|
6662
|
+
runtimeId,
|
|
6663
|
+
waitedMs,
|
|
6664
|
+
activeCount: limiter.activeCount(runtimeId),
|
|
6665
|
+
queuedCount: limiter.queuedCount(runtimeId)
|
|
6666
|
+
};
|
|
6583
6667
|
}
|
|
6584
6668
|
try {
|
|
6585
6669
|
const subsession = await runSubsession(deps.client, request, subsessionDeps);
|
|
6586
6670
|
const record = buildRecord(input, decision2, subsession, deps, correlation.decisionID, correlation.batchID);
|
|
6587
6671
|
await safeEmit(deps.sink, correlatedCostRecordToRouteEvent(record));
|
|
6672
|
+
if (queueObservation !== undefined) {
|
|
6673
|
+
const sessionID = recordSessionID(input, subsession);
|
|
6674
|
+
if (sessionID !== undefined) {
|
|
6675
|
+
const queueCorrelation = {
|
|
6676
|
+
decisionID: correlation.decisionID,
|
|
6677
|
+
agent: input.roleID
|
|
6678
|
+
};
|
|
6679
|
+
if (correlation.batchID !== undefined) {
|
|
6680
|
+
queueCorrelation.batchID = correlation.batchID;
|
|
6681
|
+
}
|
|
6682
|
+
await safeEmit(deps.sink, buildQueueEvent(queueObservation, sessionID, deps.now(), queueCorrelation));
|
|
6683
|
+
}
|
|
6684
|
+
}
|
|
6588
6685
|
return { decision: decision2, subsession, record };
|
|
6589
6686
|
} finally {
|
|
6590
6687
|
release?.();
|
|
@@ -7505,6 +7602,60 @@ function createTelemetryEventSink(options, deps) {
|
|
|
7505
7602
|
return { sink, resolution };
|
|
7506
7603
|
}
|
|
7507
7604
|
|
|
7605
|
+
// src/telemetry/queueWait.ts
|
|
7606
|
+
function percentile(sortedAsc, p) {
|
|
7607
|
+
const rank = Math.ceil(p / 100 * sortedAsc.length);
|
|
7608
|
+
const index = Math.min(Math.max(rank, 1), sortedAsc.length) - 1;
|
|
7609
|
+
return sortedAsc[index] ?? 0;
|
|
7610
|
+
}
|
|
7611
|
+
function summarizeGroup(runtimeId, waits, maxQueued, maxActive) {
|
|
7612
|
+
const sorted = [...waits].sort((a, b) => a - b);
|
|
7613
|
+
return {
|
|
7614
|
+
runtimeId,
|
|
7615
|
+
samples: sorted.length,
|
|
7616
|
+
contended: sorted.filter((wait) => wait > 0).length,
|
|
7617
|
+
p50Ms: percentile(sorted, 50),
|
|
7618
|
+
p95Ms: percentile(sorted, 95),
|
|
7619
|
+
maxMs: sorted[sorted.length - 1] ?? 0,
|
|
7620
|
+
maxQueued,
|
|
7621
|
+
maxActive
|
|
7622
|
+
};
|
|
7623
|
+
}
|
|
7624
|
+
function summarizeQueueWaits(events) {
|
|
7625
|
+
if (events.length === 0) {
|
|
7626
|
+
return;
|
|
7627
|
+
}
|
|
7628
|
+
const allWaits = [];
|
|
7629
|
+
let overallMaxQueued = 0;
|
|
7630
|
+
let overallMaxActive = 0;
|
|
7631
|
+
const byRuntime = new Map;
|
|
7632
|
+
for (const event of events) {
|
|
7633
|
+
allWaits.push(event.waitedMs);
|
|
7634
|
+
overallMaxQueued = Math.max(overallMaxQueued, event.queuedCount);
|
|
7635
|
+
overallMaxActive = Math.max(overallMaxActive, event.activeCount);
|
|
7636
|
+
let group = byRuntime.get(event.runtimeId);
|
|
7637
|
+
if (group === undefined) {
|
|
7638
|
+
group = { waits: [], maxQueued: 0, maxActive: 0 };
|
|
7639
|
+
byRuntime.set(event.runtimeId, group);
|
|
7640
|
+
}
|
|
7641
|
+
group.waits.push(event.waitedMs);
|
|
7642
|
+
group.maxQueued = Math.max(group.maxQueued, event.queuedCount);
|
|
7643
|
+
group.maxActive = Math.max(group.maxActive, event.activeCount);
|
|
7644
|
+
}
|
|
7645
|
+
const overall = summarizeGroup("*", allWaits, overallMaxQueued, overallMaxActive);
|
|
7646
|
+
const perRuntime = [...byRuntime.entries()].sort(([a], [b]) => a.localeCompare(b)).map(([runtimeId, group]) => summarizeGroup(runtimeId, group.waits, group.maxQueued, group.maxActive));
|
|
7647
|
+
return {
|
|
7648
|
+
samples: overall.samples,
|
|
7649
|
+
contended: overall.contended,
|
|
7650
|
+
p50Ms: overall.p50Ms,
|
|
7651
|
+
p95Ms: overall.p95Ms,
|
|
7652
|
+
maxMs: overall.maxMs,
|
|
7653
|
+
maxQueued: overall.maxQueued,
|
|
7654
|
+
maxActive: overall.maxActive,
|
|
7655
|
+
perRuntime
|
|
7656
|
+
};
|
|
7657
|
+
}
|
|
7658
|
+
|
|
7508
7659
|
// src/web/paths.ts
|
|
7509
7660
|
var DEFAULT_DECISIONS_PATH = ".opencode/openteam/decisions.md";
|
|
7510
7661
|
|
|
@@ -7982,6 +8133,24 @@ function isNetworkRuntime(baseURL) {
|
|
|
7982
8133
|
return false;
|
|
7983
8134
|
}
|
|
7984
8135
|
}
|
|
8136
|
+
function queueWaitSection(view) {
|
|
8137
|
+
const lines = [" local queue wait (#144, per openteam process):"];
|
|
8138
|
+
const summary = view.summary;
|
|
8139
|
+
if (summary === undefined || summary.samples === 0) {
|
|
8140
|
+
lines.push(" (no queue samples recorded yet — run an orchestrated load, then re-run doctor)");
|
|
8141
|
+
return lines;
|
|
8142
|
+
}
|
|
8143
|
+
const mark = summary.contended > 0 ? "⚠" : "✓";
|
|
8144
|
+
lines.push(` ${mark} ${summary.samples} dispatch(es), ${summary.contended} queued — ` + `p50 ${summary.p50Ms}ms · p95 ${summary.p95Ms}ms · max ${summary.maxMs}ms`);
|
|
8145
|
+
lines.push(` peak depth: ${summary.maxActive} active · ${summary.maxQueued} queued`);
|
|
8146
|
+
for (const runtime of summary.perRuntime) {
|
|
8147
|
+
lines.push(` ${runtime.runtimeId.padEnd(14)} ${runtime.samples} sample(s), ` + `${runtime.contended} queued — p50 ${runtime.p50Ms}ms · p95 ${runtime.p95Ms}ms · ` + `max ${runtime.maxMs}ms · peak ${runtime.maxActive} active/${runtime.maxQueued} queued`);
|
|
8148
|
+
}
|
|
8149
|
+
if (summary.contended > 0) {
|
|
8150
|
+
lines.push(" Lower a runtime's maxConcurrency in .opencode/openteam.json to leave slots for interactive traffic.");
|
|
8151
|
+
}
|
|
8152
|
+
return lines;
|
|
8153
|
+
}
|
|
7985
8154
|
function failureReasonText(reason, searchedPaths) {
|
|
7986
8155
|
switch (reason) {
|
|
7987
8156
|
case "provider-not-configured": {
|
|
@@ -8141,6 +8310,20 @@ function rosterHealthSection(audit, loadError, rosterRoleCount) {
|
|
|
8141
8310
|
}
|
|
8142
8311
|
return lines;
|
|
8143
8312
|
}
|
|
8313
|
+
function gitignoreSection(gitignore) {
|
|
8314
|
+
if (gitignore.missingRules.length === 0) {
|
|
8315
|
+
return [
|
|
8316
|
+
" gitignore:",
|
|
8317
|
+
` ✓ ${gitignore.path} ignores openteam's generated runtime state.`
|
|
8318
|
+
];
|
|
8319
|
+
}
|
|
8320
|
+
const quoted = gitignore.missingRules.map((rule) => `\`${rule}\``).join(", ");
|
|
8321
|
+
const lines = [" gitignore:"];
|
|
8322
|
+
lines.push(gitignore.exists ? ` ✗ ${gitignore.path} is missing openteam ignore rule(s): ${gitignore.missingRules.join(", ")}` : ` ✗ ${gitignore.path} does not exist, so no openteam ignore rule(s) are in place: ${gitignore.missingRules.join(", ")}`);
|
|
8323
|
+
lines.push(" problem: openteam writes machine-local runtime output (telemetry, graph state, soak recordings) that must not be committed.");
|
|
8324
|
+
lines.push(` remedy: add ${quoted} to ${gitignore.path} (or re-run \`openteam setup\`), then re-run \`openteam doctor\`.`);
|
|
8325
|
+
return lines;
|
|
8326
|
+
}
|
|
8144
8327
|
function agentModelsSection(diagnostics, searchedPaths) {
|
|
8145
8328
|
const lines = [" agent models:"];
|
|
8146
8329
|
if (diagnostics.length === 0) {
|
|
@@ -8184,10 +8367,16 @@ function renderDoctor(input) {
|
|
|
8184
8367
|
lines.push(` ⚠ ${snapshot.id} is reached over the network: its ${cap}-slot cap is enforced per openteam process, so`, " two processes at once (an opencode session plus 'openteam console') can together exceed it.");
|
|
8185
8368
|
}
|
|
8186
8369
|
lines.push(` telemetry: ${input.telemetryPath} — ${input.telemetryRecords} record(s)`);
|
|
8370
|
+
if (input.queueWait !== undefined) {
|
|
8371
|
+
lines.push(...queueWaitSection(input.queueWait));
|
|
8372
|
+
}
|
|
8187
8373
|
if (input.diagnostics !== undefined) {
|
|
8188
8374
|
lines.push(...diagnosticsSection(input.diagnostics));
|
|
8189
8375
|
}
|
|
8190
8376
|
lines.push(...rosterHealthSection(input.rosterAudit, input.rosterLoadError, input.rosterRoleCount));
|
|
8377
|
+
if (input.gitignore !== undefined) {
|
|
8378
|
+
lines.push(...gitignoreSection(input.gitignore));
|
|
8379
|
+
}
|
|
8191
8380
|
if (input.otelBackend !== undefined) {
|
|
8192
8381
|
const otel = input.otelBackend;
|
|
8193
8382
|
lines.push(" opentelemetry:");
|
|
@@ -8453,6 +8642,143 @@ function renderMigration(report) {
|
|
|
8453
8642
|
// src/commands/purge.ts
|
|
8454
8643
|
import * as path3 from "node:path";
|
|
8455
8644
|
|
|
8645
|
+
// src/storage/graph/soakRecorder.ts
|
|
8646
|
+
var SOAK_EVIDENCE_DIR = ".opencode/soak";
|
|
8647
|
+
var SOAK_RECORDER_BASE_DIR = ".opencode";
|
|
8648
|
+
var SOAK_RECORDER_ID_FILENAME = "soak-recorder-id";
|
|
8649
|
+
var SOAK_RECORDER_ID_PATH = `${SOAK_RECORDER_BASE_DIR}/${SOAK_RECORDER_ID_FILENAME}`;
|
|
8650
|
+
async function ensureRecorderId(ports, basePath) {
|
|
8651
|
+
const idPath = `${basePath}/${SOAK_RECORDER_ID_FILENAME}`;
|
|
8652
|
+
if (await ports.fs.exists(idPath)) {
|
|
8653
|
+
const id = (await ports.fs.readFile(idPath)).trim();
|
|
8654
|
+
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(id)) {
|
|
8655
|
+
return id;
|
|
8656
|
+
}
|
|
8657
|
+
}
|
|
8658
|
+
const newId = ports.id.randomUUID();
|
|
8659
|
+
await ports.fs.writeFile(idPath, newId);
|
|
8660
|
+
return newId;
|
|
8661
|
+
}
|
|
8662
|
+
async function readAllChains(ports, evidenceDir) {
|
|
8663
|
+
if (!await ports.fs.exists(evidenceDir)) {
|
|
8664
|
+
return { chains: [], rejectedLines: 0, rejections: [] };
|
|
8665
|
+
}
|
|
8666
|
+
const files = await ports.fs.readDir(evidenceDir);
|
|
8667
|
+
const chains = [];
|
|
8668
|
+
const rejections = [];
|
|
8669
|
+
for (const file of files) {
|
|
8670
|
+
if (!file.endsWith(".jsonl")) {
|
|
8671
|
+
continue;
|
|
8672
|
+
}
|
|
8673
|
+
const content = await ports.fs.readFile(`${evidenceDir}/${file}`);
|
|
8674
|
+
const lines = content.split(`
|
|
8675
|
+
`);
|
|
8676
|
+
const observations = [];
|
|
8677
|
+
for (const [index, line] of lines.entries()) {
|
|
8678
|
+
if (line.trim().length === 0) {
|
|
8679
|
+
continue;
|
|
8680
|
+
}
|
|
8681
|
+
const lineNumber = index + 1;
|
|
8682
|
+
let rawParsed;
|
|
8683
|
+
try {
|
|
8684
|
+
rawParsed = JSON.parse(line);
|
|
8685
|
+
} catch {
|
|
8686
|
+
rejections.push({ file, lineNumber, detail: "invalid JSON" });
|
|
8687
|
+
continue;
|
|
8688
|
+
}
|
|
8689
|
+
const parsed = SoakObservationSchema.safeParse(rawParsed);
|
|
8690
|
+
if (parsed.success) {
|
|
8691
|
+
observations.push(parsed.data);
|
|
8692
|
+
} else {
|
|
8693
|
+
rejections.push({
|
|
8694
|
+
file,
|
|
8695
|
+
lineNumber,
|
|
8696
|
+
detail: formatSoakObservationIssues(parsed.error.issues)
|
|
8697
|
+
});
|
|
8698
|
+
}
|
|
8699
|
+
}
|
|
8700
|
+
if (observations.length > 0) {
|
|
8701
|
+
chains.push(observations);
|
|
8702
|
+
}
|
|
8703
|
+
}
|
|
8704
|
+
return { chains, rejectedLines: rejections.length, rejections };
|
|
8705
|
+
}
|
|
8706
|
+
function formatSoakObservationIssues(issues) {
|
|
8707
|
+
return issues.map((i) => `${i.path.join(".")}: ${i.code}`).join("; ");
|
|
8708
|
+
}
|
|
8709
|
+
function canonicalObservationPreimage2(obs) {
|
|
8710
|
+
return JSON.stringify({
|
|
8711
|
+
version: obs.version,
|
|
8712
|
+
seq: obs.seq,
|
|
8713
|
+
timestamp: obs.timestamp,
|
|
8714
|
+
platform: obs.platform,
|
|
8715
|
+
opencodeVersion: obs.opencodeVersion,
|
|
8716
|
+
provenance: obs.provenance,
|
|
8717
|
+
traceDigest: obs.traceDigest,
|
|
8718
|
+
criticalDivergences: obs.criticalDivergences,
|
|
8719
|
+
privacy: obs.privacy,
|
|
8720
|
+
duplicateEffects: obs.duplicateEffects,
|
|
8721
|
+
modelVerification: obs.modelVerification
|
|
8722
|
+
});
|
|
8723
|
+
}
|
|
8724
|
+
async function appendObservation(ports, evidenceDir, recorderId, input) {
|
|
8725
|
+
if (input.opencodeVersion === "unknown") {
|
|
8726
|
+
throw new Error("opencode-version-unreadable");
|
|
8727
|
+
}
|
|
8728
|
+
await ports.fs.mkdir(evidenceDir);
|
|
8729
|
+
const chainPath = `${evidenceDir}/${recorderId}.jsonl`;
|
|
8730
|
+
let seq = 0;
|
|
8731
|
+
let prevChainDigest = "0".repeat(64);
|
|
8732
|
+
if (await ports.fs.exists(chainPath)) {
|
|
8733
|
+
const content = await ports.fs.readFile(chainPath);
|
|
8734
|
+
const lines = content.split(`
|
|
8735
|
+
`).filter((l) => l.trim().length > 0);
|
|
8736
|
+
if (lines.length > 0) {
|
|
8737
|
+
const lastLine = lines[lines.length - 1];
|
|
8738
|
+
let rawParsed;
|
|
8739
|
+
try {
|
|
8740
|
+
rawParsed = JSON.parse(lastLine);
|
|
8741
|
+
} catch {
|
|
8742
|
+
throw new Error("corrupt chain tail: invalid JSON");
|
|
8743
|
+
}
|
|
8744
|
+
const parsed = SoakObservationSchema.safeParse(rawParsed);
|
|
8745
|
+
if (!parsed.success) {
|
|
8746
|
+
const detail = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.code}`).join("; ");
|
|
8747
|
+
throw new Error(`corrupt chain tail: ${detail}`);
|
|
8748
|
+
}
|
|
8749
|
+
seq = parsed.data.seq + 1;
|
|
8750
|
+
prevChainDigest = parsed.data.chainDigest;
|
|
8751
|
+
}
|
|
8752
|
+
}
|
|
8753
|
+
const timestamp = ports.clock.now();
|
|
8754
|
+
const base = {
|
|
8755
|
+
version: 1,
|
|
8756
|
+
seq,
|
|
8757
|
+
timestamp,
|
|
8758
|
+
platform: input.platform,
|
|
8759
|
+
opencodeVersion: input.opencodeVersion,
|
|
8760
|
+
provenance: input.provenance,
|
|
8761
|
+
traceDigest: input.traceDigest,
|
|
8762
|
+
criticalDivergences: input.criticalDivergences,
|
|
8763
|
+
privacy: input.privacy,
|
|
8764
|
+
duplicateEffects: input.duplicateEffects,
|
|
8765
|
+
modelVerification: input.modelVerification
|
|
8766
|
+
};
|
|
8767
|
+
const preimage = canonicalObservationPreimage2(base);
|
|
8768
|
+
const chainDigest = sha256Hex(prevChainDigest + preimage);
|
|
8769
|
+
const observation = { ...base, chainDigest };
|
|
8770
|
+
assertReferenceOnly(observation);
|
|
8771
|
+
const line = JSON.stringify(observation);
|
|
8772
|
+
if (await ports.fs.exists(chainPath)) {
|
|
8773
|
+
const existing = await ports.fs.readFile(chainPath);
|
|
8774
|
+
await ports.fs.writeFile(chainPath, `${existing}
|
|
8775
|
+
${line}`);
|
|
8776
|
+
} else {
|
|
8777
|
+
await ports.fs.writeFile(chainPath, line);
|
|
8778
|
+
}
|
|
8779
|
+
return observation;
|
|
8780
|
+
}
|
|
8781
|
+
|
|
8456
8782
|
// src/commands/setup.ts
|
|
8457
8783
|
var OPENTEAM_PLUGIN_SPEC = "@jmanuelcorral/openteam";
|
|
8458
8784
|
var OPENCODE_GITIGNORE_PATH = ".opencode/.gitignore";
|
|
@@ -8464,6 +8790,11 @@ var GENERATED_OPENTEAM_STATE_PATHS = [
|
|
|
8464
8790
|
DEFAULT_STORAGE_SQLITE_INDEX_PATH,
|
|
8465
8791
|
DEFAULT_MEMORY_INDEX_PATH
|
|
8466
8792
|
];
|
|
8793
|
+
var GENERATED_OPENTEAM_STATE_GROUPS = [
|
|
8794
|
+
{ kind: "dir", paths: GENERATED_OPENTEAM_STATE_PATHS },
|
|
8795
|
+
{ kind: "dir", paths: [SOAK_EVIDENCE_DIR] },
|
|
8796
|
+
{ kind: "file", paths: [SOAK_RECORDER_ID_PATH] }
|
|
8797
|
+
];
|
|
8467
8798
|
var OPENCODE_DIR = ".opencode";
|
|
8468
8799
|
var AGENT_DIR = `${OPENCODE_DIR}/agent`;
|
|
8469
8800
|
function splitSetupPath(path3) {
|
|
@@ -8484,18 +8815,27 @@ function commonGeneratedStateRoot(paths) {
|
|
|
8484
8815
|
}
|
|
8485
8816
|
return common.join("/");
|
|
8486
8817
|
}
|
|
8487
|
-
function generatedStateIgnoreRule() {
|
|
8488
|
-
const root = commonGeneratedStateRoot(
|
|
8818
|
+
function generatedStateIgnoreRule(group) {
|
|
8819
|
+
const root = commonGeneratedStateRoot(group.paths);
|
|
8489
8820
|
const opencodePrefix = `${OPENCODE_DIR}/`;
|
|
8490
8821
|
if (!root.startsWith(opencodePrefix)) {
|
|
8491
8822
|
throw new Error(`generated openteam state path is outside ${OPENCODE_DIR}`);
|
|
8492
8823
|
}
|
|
8493
|
-
|
|
8824
|
+
const rule = root.slice(opencodePrefix.length);
|
|
8825
|
+
return group.kind === "dir" ? `${rule}/` : rule;
|
|
8494
8826
|
}
|
|
8495
|
-
var OPENCODE_GITIGNORE_GENERATED_STATE_RULES =
|
|
8496
|
-
generatedStateIgnoreRule()
|
|
8497
|
-
];
|
|
8827
|
+
var OPENCODE_GITIGNORE_GENERATED_STATE_RULES = GENERATED_OPENTEAM_STATE_GROUPS.map(generatedStateIgnoreRule);
|
|
8498
8828
|
var LEGACY_OPENCODE_GITIGNORE_STATE_RULES = ["openteam/"];
|
|
8829
|
+
function normalizeGitignoreRule(line) {
|
|
8830
|
+
return line.trim().replace(/^\/+/, "");
|
|
8831
|
+
}
|
|
8832
|
+
function activeGitignoreRules(gitignore) {
|
|
8833
|
+
return gitignore.split(/\r?\n/).map(normalizeGitignoreRule).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
8834
|
+
}
|
|
8835
|
+
function missingGeneratedStateRules(gitignore) {
|
|
8836
|
+
const present = new Set(activeGitignoreRules(gitignore));
|
|
8837
|
+
return OPENCODE_GITIGNORE_GENERATED_STATE_RULES.filter((rule) => !present.has(rule));
|
|
8838
|
+
}
|
|
8499
8839
|
|
|
8500
8840
|
// src/commands/purge.ts
|
|
8501
8841
|
var PURGE_CATEGORY_IDS = [
|
|
@@ -8543,7 +8883,7 @@ function resolveStaticPurgeItems(config, paths) {
|
|
|
8543
8883
|
...paths.opencodeConfigPaths.map((p) => ({ kind: "opencode-config", path: p })),
|
|
8544
8884
|
{ kind: "gitignore", path: paths.gitignorePath }
|
|
8545
8885
|
],
|
|
8546
|
-
roster: [],
|
|
8886
|
+
roster: [{ kind: "file", path: OPENTEAM_ROSTER_PATH }],
|
|
8547
8887
|
decisions: [
|
|
8548
8888
|
{ kind: "file", path: paths.decisionsPath },
|
|
8549
8889
|
{ kind: "file", path: paths.telemetryPath }
|
|
@@ -8835,8 +9175,12 @@ function categoryPrompt(id, items) {
|
|
|
8835
9175
|
return "Remove openteam configuration and wiring?";
|
|
8836
9176
|
case "roster": {
|
|
8837
9177
|
const names = items.filter((item) => item.kind === "agent").map((item) => item.name);
|
|
8838
|
-
const
|
|
8839
|
-
|
|
9178
|
+
const hasRegistry = items.some((item) => item.kind === "file" && item.path === OPENTEAM_ROSTER_PATH);
|
|
9179
|
+
const plural = names.length === 1 ? "" : "s";
|
|
9180
|
+
const agentNames = names.length > 0 ? `: ${names.join(", ")}` : "";
|
|
9181
|
+
const agentClause = `${names.length} user agent${plural}${agentNames}`;
|
|
9182
|
+
const parts = hasRegistry ? [`roster registry (${OPENTEAM_ROSTER_PATH})`, agentClause] : [agentClause];
|
|
9183
|
+
return `Remove the agent roster? (${parts.join("; ")})`;
|
|
8840
9184
|
}
|
|
8841
9185
|
case "decisions":
|
|
8842
9186
|
return "Remove decisions and telemetry?";
|
|
@@ -8898,7 +9242,9 @@ async function collectCategories(input) {
|
|
|
8898
9242
|
const { runtime, config, paths } = input;
|
|
8899
9243
|
const staticItems = resolveStaticPurgeItems(config, paths);
|
|
8900
9244
|
const agents = await runtime.port.listAgents();
|
|
8901
|
-
const
|
|
9245
|
+
const discoveredAgents = agents.filter((agent) => path3.resolve(agent.path) !== path3.resolve(paths.orchestratorAgentPath)).map((agent) => ({ kind: "agent", path: agent.path, name: agent.name }));
|
|
9246
|
+
const rosterRegistry = await presentItemsFor(runtime.port, staticItems.roster);
|
|
9247
|
+
const rosterItems = [...rosterRegistry, ...discoveredAgents];
|
|
8902
9248
|
const worktrees = await runtime.port.listWorktrees();
|
|
8903
9249
|
const worktreeItems = worktrees.map((worktree) => ({
|
|
8904
9250
|
kind: "worktree",
|
|
@@ -10051,6 +10397,15 @@ async function runCli(argv, deps) {
|
|
|
10051
10397
|
...defaultModel !== undefined ? { defaultModel } : {}
|
|
10052
10398
|
});
|
|
10053
10399
|
const legacyLayout = deps.detectLegacyLayout !== undefined ? await deps.detectLegacyLayout() : undefined;
|
|
10400
|
+
let gitignoreCoverage;
|
|
10401
|
+
if (deps.readGitignore !== undefined) {
|
|
10402
|
+
const raw = await deps.readGitignore();
|
|
10403
|
+
gitignoreCoverage = {
|
|
10404
|
+
path: OPENCODE_GITIGNORE_PATH,
|
|
10405
|
+
exists: raw !== undefined,
|
|
10406
|
+
missingRules: missingGeneratedStateRules(raw ?? "")
|
|
10407
|
+
};
|
|
10408
|
+
}
|
|
10054
10409
|
const otelBackend = describeOtelBackendForDoctor({
|
|
10055
10410
|
backend: "opentelemetry",
|
|
10056
10411
|
connectionEnv: "APPLICATIONINSIGHTS_CONNECTION_STRING"
|
|
@@ -10067,6 +10422,12 @@ async function runCli(argv, deps) {
|
|
|
10067
10422
|
rosterLoadError = err instanceof Error ? err.message : String(err);
|
|
10068
10423
|
}
|
|
10069
10424
|
}
|
|
10425
|
+
let queueWait;
|
|
10426
|
+
if (deps.readQueueEvents !== undefined) {
|
|
10427
|
+
const queueEvents = await deps.readQueueEvents();
|
|
10428
|
+
const summary = summarizeQueueWaits(queueEvents);
|
|
10429
|
+
queueWait = summary !== undefined ? { summary } : {};
|
|
10430
|
+
}
|
|
10070
10431
|
return {
|
|
10071
10432
|
exitCode: 0,
|
|
10072
10433
|
stdout: renderDoctor({
|
|
@@ -10076,6 +10437,7 @@ async function runCli(argv, deps) {
|
|
|
10076
10437
|
telemetryRecords: records.length,
|
|
10077
10438
|
agentModels,
|
|
10078
10439
|
opencodeConfigPaths,
|
|
10440
|
+
...queueWait !== undefined ? { queueWait } : {},
|
|
10079
10441
|
...rosterAudit !== undefined ? { rosterAudit } : {},
|
|
10080
10442
|
...rosterRoleCount !== undefined ? { rosterRoleCount } : {},
|
|
10081
10443
|
...rosterLoadError !== undefined ? { rosterLoadError } : {},
|
|
@@ -10083,7 +10445,8 @@ async function runCli(argv, deps) {
|
|
|
10083
10445
|
...opencodeConfigError !== undefined ? { opencodeConfigError } : {},
|
|
10084
10446
|
...cacheEntries !== undefined ? { cacheEntries, cliVersion: deps.version } : {},
|
|
10085
10447
|
...legacyLayout !== undefined && legacyLayout.length > 0 ? { legacyLayout } : {},
|
|
10086
|
-
...otelBackend !== undefined ? { otelBackend } : {}
|
|
10448
|
+
...otelBackend !== undefined ? { otelBackend } : {},
|
|
10449
|
+
...gitignoreCoverage !== undefined ? { gitignore: gitignoreCoverage } : {}
|
|
10087
10450
|
})
|
|
10088
10451
|
};
|
|
10089
10452
|
}
|
|
@@ -10759,139 +11122,6 @@ function compareShadowRun(legacy, shadow) {
|
|
|
10759
11122
|
};
|
|
10760
11123
|
}
|
|
10761
11124
|
|
|
10762
|
-
// src/storage/graph/soakRecorder.ts
|
|
10763
|
-
async function ensureRecorderId(ports, basePath) {
|
|
10764
|
-
const idPath = `${basePath}/soak-recorder-id`;
|
|
10765
|
-
if (await ports.fs.exists(idPath)) {
|
|
10766
|
-
const id = (await ports.fs.readFile(idPath)).trim();
|
|
10767
|
-
if (/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(id)) {
|
|
10768
|
-
return id;
|
|
10769
|
-
}
|
|
10770
|
-
}
|
|
10771
|
-
const newId = ports.id.randomUUID();
|
|
10772
|
-
await ports.fs.writeFile(idPath, newId);
|
|
10773
|
-
return newId;
|
|
10774
|
-
}
|
|
10775
|
-
async function readAllChains(ports, evidenceDir) {
|
|
10776
|
-
if (!await ports.fs.exists(evidenceDir)) {
|
|
10777
|
-
return { chains: [], rejectedLines: 0, rejections: [] };
|
|
10778
|
-
}
|
|
10779
|
-
const files = await ports.fs.readDir(evidenceDir);
|
|
10780
|
-
const chains = [];
|
|
10781
|
-
const rejections = [];
|
|
10782
|
-
for (const file of files) {
|
|
10783
|
-
if (!file.endsWith(".jsonl")) {
|
|
10784
|
-
continue;
|
|
10785
|
-
}
|
|
10786
|
-
const content = await ports.fs.readFile(`${evidenceDir}/${file}`);
|
|
10787
|
-
const lines = content.split(`
|
|
10788
|
-
`);
|
|
10789
|
-
const observations = [];
|
|
10790
|
-
for (const [index, line] of lines.entries()) {
|
|
10791
|
-
if (line.trim().length === 0) {
|
|
10792
|
-
continue;
|
|
10793
|
-
}
|
|
10794
|
-
const lineNumber = index + 1;
|
|
10795
|
-
let rawParsed;
|
|
10796
|
-
try {
|
|
10797
|
-
rawParsed = JSON.parse(line);
|
|
10798
|
-
} catch {
|
|
10799
|
-
rejections.push({ file, lineNumber, detail: "invalid JSON" });
|
|
10800
|
-
continue;
|
|
10801
|
-
}
|
|
10802
|
-
const parsed = SoakObservationSchema.safeParse(rawParsed);
|
|
10803
|
-
if (parsed.success) {
|
|
10804
|
-
observations.push(parsed.data);
|
|
10805
|
-
} else {
|
|
10806
|
-
rejections.push({
|
|
10807
|
-
file,
|
|
10808
|
-
lineNumber,
|
|
10809
|
-
detail: formatSoakObservationIssues(parsed.error.issues)
|
|
10810
|
-
});
|
|
10811
|
-
}
|
|
10812
|
-
}
|
|
10813
|
-
if (observations.length > 0) {
|
|
10814
|
-
chains.push(observations);
|
|
10815
|
-
}
|
|
10816
|
-
}
|
|
10817
|
-
return { chains, rejectedLines: rejections.length, rejections };
|
|
10818
|
-
}
|
|
10819
|
-
function formatSoakObservationIssues(issues) {
|
|
10820
|
-
return issues.map((i) => `${i.path.join(".")}: ${i.code}`).join("; ");
|
|
10821
|
-
}
|
|
10822
|
-
function canonicalObservationPreimage2(obs) {
|
|
10823
|
-
return JSON.stringify({
|
|
10824
|
-
version: obs.version,
|
|
10825
|
-
seq: obs.seq,
|
|
10826
|
-
timestamp: obs.timestamp,
|
|
10827
|
-
platform: obs.platform,
|
|
10828
|
-
opencodeVersion: obs.opencodeVersion,
|
|
10829
|
-
provenance: obs.provenance,
|
|
10830
|
-
traceDigest: obs.traceDigest,
|
|
10831
|
-
criticalDivergences: obs.criticalDivergences,
|
|
10832
|
-
privacy: obs.privacy,
|
|
10833
|
-
duplicateEffects: obs.duplicateEffects,
|
|
10834
|
-
modelVerification: obs.modelVerification
|
|
10835
|
-
});
|
|
10836
|
-
}
|
|
10837
|
-
async function appendObservation(ports, evidenceDir, recorderId, input) {
|
|
10838
|
-
if (input.opencodeVersion === "unknown") {
|
|
10839
|
-
throw new Error("opencode-version-unreadable");
|
|
10840
|
-
}
|
|
10841
|
-
await ports.fs.mkdir(evidenceDir);
|
|
10842
|
-
const chainPath = `${evidenceDir}/${recorderId}.jsonl`;
|
|
10843
|
-
let seq = 0;
|
|
10844
|
-
let prevChainDigest = "0".repeat(64);
|
|
10845
|
-
if (await ports.fs.exists(chainPath)) {
|
|
10846
|
-
const content = await ports.fs.readFile(chainPath);
|
|
10847
|
-
const lines = content.split(`
|
|
10848
|
-
`).filter((l) => l.trim().length > 0);
|
|
10849
|
-
if (lines.length > 0) {
|
|
10850
|
-
const lastLine = lines[lines.length - 1];
|
|
10851
|
-
let rawParsed;
|
|
10852
|
-
try {
|
|
10853
|
-
rawParsed = JSON.parse(lastLine);
|
|
10854
|
-
} catch {
|
|
10855
|
-
throw new Error("corrupt chain tail: invalid JSON");
|
|
10856
|
-
}
|
|
10857
|
-
const parsed = SoakObservationSchema.safeParse(rawParsed);
|
|
10858
|
-
if (!parsed.success) {
|
|
10859
|
-
const detail = parsed.error.issues.map((i) => `${i.path.join(".")}: ${i.code}`).join("; ");
|
|
10860
|
-
throw new Error(`corrupt chain tail: ${detail}`);
|
|
10861
|
-
}
|
|
10862
|
-
seq = parsed.data.seq + 1;
|
|
10863
|
-
prevChainDigest = parsed.data.chainDigest;
|
|
10864
|
-
}
|
|
10865
|
-
}
|
|
10866
|
-
const timestamp = ports.clock.now();
|
|
10867
|
-
const base = {
|
|
10868
|
-
version: 1,
|
|
10869
|
-
seq,
|
|
10870
|
-
timestamp,
|
|
10871
|
-
platform: input.platform,
|
|
10872
|
-
opencodeVersion: input.opencodeVersion,
|
|
10873
|
-
provenance: input.provenance,
|
|
10874
|
-
traceDigest: input.traceDigest,
|
|
10875
|
-
criticalDivergences: input.criticalDivergences,
|
|
10876
|
-
privacy: input.privacy,
|
|
10877
|
-
duplicateEffects: input.duplicateEffects,
|
|
10878
|
-
modelVerification: input.modelVerification
|
|
10879
|
-
};
|
|
10880
|
-
const preimage = canonicalObservationPreimage2(base);
|
|
10881
|
-
const chainDigest = sha256Hex(prevChainDigest + preimage);
|
|
10882
|
-
const observation = { ...base, chainDigest };
|
|
10883
|
-
assertReferenceOnly(observation);
|
|
10884
|
-
const line = JSON.stringify(observation);
|
|
10885
|
-
if (await ports.fs.exists(chainPath)) {
|
|
10886
|
-
const existing = await ports.fs.readFile(chainPath);
|
|
10887
|
-
await ports.fs.writeFile(chainPath, `${existing}
|
|
10888
|
-
${line}`);
|
|
10889
|
-
} else {
|
|
10890
|
-
await ports.fs.writeFile(chainPath, line);
|
|
10891
|
-
}
|
|
10892
|
-
return observation;
|
|
10893
|
-
}
|
|
10894
|
-
|
|
10895
11125
|
// src/plugin/soakObserver.ts
|
|
10896
11126
|
function resolvePlatform(platform) {
|
|
10897
11127
|
if (platform === "win32")
|
|
@@ -13036,7 +13266,7 @@ var createOtlpSpanExporter = (connection, config) => {
|
|
|
13036
13266
|
// package.json
|
|
13037
13267
|
var package_default = {
|
|
13038
13268
|
name: "@jmanuelcorral/openteam",
|
|
13039
|
-
version: "0.
|
|
13269
|
+
version: "0.14.0",
|
|
13040
13270
|
packageManager: "bun@1.3.14",
|
|
13041
13271
|
description: "Cost-aware, local-first routing plugin for opencode with cheapest-capable frontier fallback and multi-agent orchestration.",
|
|
13042
13272
|
license: "MIT",
|
|
@@ -13212,7 +13442,6 @@ function warnSoakEvidenceRejections(result, warn) {
|
|
|
13212
13442
|
var SHADOW_CERT_PATH = "artifacts/graph-shadow-certificate.json";
|
|
13213
13443
|
var RELEASE_CERT_PATH = "artifacts/graph-release-certificate.json";
|
|
13214
13444
|
var SOAK_CERT_PATH = "artifacts/graph-soak-certificate.json";
|
|
13215
|
-
var SOAK_EVIDENCE_DIR = ".opencode/soak";
|
|
13216
13445
|
async function readAndParseCertificate(storage, path4, parse2, digestExtractor) {
|
|
13217
13446
|
try {
|
|
13218
13447
|
const content = await storage.read(path4);
|
|
@@ -13735,14 +13964,14 @@ var server = async (ctx, rawOptions) => {
|
|
|
13735
13964
|
let shadowDeps;
|
|
13736
13965
|
if (graphConfig.effectiveMode !== "off") {
|
|
13737
13966
|
const recorderPorts = recorderPortsFromStorage(storage);
|
|
13738
|
-
const recorderId = await ensureRecorderId(recorderPorts,
|
|
13967
|
+
const recorderId = await ensureRecorderId(recorderPorts, SOAK_RECORDER_BASE_DIR);
|
|
13739
13968
|
shadowDeps = {
|
|
13740
13969
|
gate: {
|
|
13741
13970
|
mode: graphConfig.configuredMode,
|
|
13742
13971
|
killSwitch: graphConfig.killSwitch
|
|
13743
13972
|
},
|
|
13744
13973
|
ports: recorderPorts,
|
|
13745
|
-
evidenceDir:
|
|
13974
|
+
evidenceDir: SOAK_EVIDENCE_DIR,
|
|
13746
13975
|
recorderId,
|
|
13747
13976
|
resolveOpencodeVersion: getOpencodeVersion,
|
|
13748
13977
|
criticalCodes: DEFAULT_SOAK_POLICY.criticalDivergenceCodes
|
|
@@ -13777,7 +14006,8 @@ var server = async (ctx, rawOptions) => {
|
|
|
13777
14006
|
return reachableRuntimeIds(snapshots);
|
|
13778
14007
|
};
|
|
13779
14008
|
const runtimeLimiter = createRuntimeConcurrencyLimiter({
|
|
13780
|
-
capacityFor: (id) => config.local.runtimes.find((runtime) => runtime.id === id)?.maxConcurrency ?? DEFAULT_LOCAL_MAX_CONCURRENCY
|
|
14009
|
+
capacityFor: (id) => config.local.runtimes.find((runtime) => runtime.id === id)?.maxConcurrency ?? DEFAULT_LOCAL_MAX_CONCURRENCY,
|
|
14010
|
+
now
|
|
13781
14011
|
});
|
|
13782
14012
|
const orchestrateTool = createOrchestrateTool({
|
|
13783
14013
|
client: orchestrateClient,
|