@jmanuelcorral/openteam 0.9.0 → 0.9.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/dist/index.js CHANGED
@@ -98,8 +98,8 @@ var AgentRoleProfileSchema = z.object({
98
98
  localRuntimes: z.array(z.enum(["ollama", "lmstudio", "foundry-local", "lemonade"])).optional()
99
99
  }).strict();
100
100
  var TEAM_ROLES = {
101
- rusty: {
102
- roleID: "rusty",
101
+ architect: {
102
+ roleID: "architect",
103
103
  opencodeAgent: "architect",
104
104
  defaultTier: "hard",
105
105
  minReasoningTier: 5,
@@ -108,8 +108,8 @@ var TEAM_ROLES = {
108
108
  preferredFrontierModels: ["auto"],
109
109
  localFirst: false
110
110
  },
111
- livingston: {
112
- roleID: "livingston",
111
+ integration: {
112
+ roleID: "integration",
113
113
  opencodeAgent: "integration",
114
114
  defaultTier: "moderate",
115
115
  minReasoningTier: 4,
@@ -118,8 +118,8 @@ var TEAM_ROLES = {
118
118
  preferredFrontierModels: ["auto"],
119
119
  localFirst: false
120
120
  },
121
- yen: {
122
- roleID: "yen",
121
+ "local-runtime": {
122
+ roleID: "local-runtime",
123
123
  opencodeAgent: "local-runtime",
124
124
  defaultTier: "moderate",
125
125
  minReasoningTier: 3,
@@ -128,8 +128,8 @@ var TEAM_ROLES = {
128
128
  preferredFrontierModels: ["auto"],
129
129
  localFirst: true
130
130
  },
131
- basher: {
132
- roleID: "basher",
131
+ "routing-cost": {
132
+ roleID: "routing-cost",
133
133
  opencodeAgent: "routing-cost",
134
134
  defaultTier: "moderate",
135
135
  minReasoningTier: 3,
@@ -171,8 +171,8 @@ var TEAM_ROLES = {
171
171
  localFirst: true,
172
172
  requiresLocalRuntime: true
173
173
  },
174
- linus: {
175
- roleID: "linus",
174
+ tester: {
175
+ roleID: "tester",
176
176
  opencodeAgent: "tester",
177
177
  defaultTier: "simple",
178
178
  minReasoningTier: 2,
@@ -192,8 +192,35 @@ var TEAM_ROLES = {
192
192
  localFirst: true
193
193
  }
194
194
  };
195
+ var LEGACY_TEAM_ROLE_ALIASES = {
196
+ rusty: "architect",
197
+ livingston: "integration",
198
+ yen: "local-runtime",
199
+ basher: "routing-cost",
200
+ linus: "tester"
201
+ };
202
+ var KNOWN_NON_WORKER_ROLE_IDS = ["openteam"];
203
+ function isKnownNonWorkerRole(roleID) {
204
+ return KNOWN_NON_WORKER_ROLE_IDS.includes(roleID);
205
+ }
206
+ function getConfiguredRoleProfileByAlias(roleID, roles) {
207
+ if (roles === undefined) {
208
+ return;
209
+ }
210
+ for (const key of Object.keys(roles).sort()) {
211
+ const profile = roles[key];
212
+ if (profile?.opencodeAgent === roleID) {
213
+ return profile;
214
+ }
215
+ }
216
+ return;
217
+ }
218
+ function getLegacyTeamRoleProfile(roleID) {
219
+ const canonicalRoleID = LEGACY_TEAM_ROLE_ALIASES[roleID];
220
+ return canonicalRoleID === undefined ? undefined : TEAM_ROLES[canonicalRoleID];
221
+ }
195
222
  function getRoleProfile(roleID, configuredRoles) {
196
- return configuredRoles?.[roleID] ?? TEAM_ROLES[roleID];
223
+ return configuredRoles?.[roleID] ?? TEAM_ROLES[roleID] ?? getConfiguredRoleProfileByAlias(roleID, configuredRoles) ?? getLegacyTeamRoleProfile(roleID);
197
224
  }
198
225
  function synthesiseFallbackProfile(roleID) {
199
226
  return {
@@ -2878,6 +2905,50 @@ var ModelCapabilityProfileSchema = z10.object({
2878
2905
  availability: z10.enum(["available", "degraded", "unavailable"])
2879
2906
  }).strict();
2880
2907
 
2908
+ // src/telemetry/diagnostics.ts
2909
+ var DIAGNOSTIC_CODES = [
2910
+ "availability-refresh-failed",
2911
+ "config-missing",
2912
+ "graph-gate-denied",
2913
+ "memory-extraction-warning",
2914
+ "memory-runtime-warning",
2915
+ "opencode-server-url",
2916
+ "opencode-version-read-failed",
2917
+ "privacy-inert",
2918
+ "soak-evidence-rejected",
2919
+ "telemetry-warning",
2920
+ "telemetry-write-failed",
2921
+ "unknown-role"
2922
+ ];
2923
+ function diagnosticDescription(code) {
2924
+ switch (code) {
2925
+ case "availability-refresh-failed":
2926
+ return "Local runtime availability refresh failed; routing will continue with the previous or empty availability snapshot.";
2927
+ case "config-missing":
2928
+ return "openteam did not find its runtime config file; defaults or plugin options were used.";
2929
+ case "graph-gate-denied":
2930
+ return "The graph cutover gate denied active mode and openteam degraded graph mode to off.";
2931
+ case "memory-extraction-warning":
2932
+ return "Semantic memory extraction reported a non-fatal local-runtime warning; no raw conversation content was recorded.";
2933
+ case "memory-runtime-warning":
2934
+ return "Semantic memory runtime setup reported a non-fatal warning; memory recall may be incomplete.";
2935
+ case "opencode-server-url":
2936
+ return "openteam observed the opencode server URL during plugin initialization.";
2937
+ case "opencode-version-read-failed":
2938
+ return "openteam could not read the live opencode version; graph gates fail closed when this happens.";
2939
+ case "privacy-inert":
2940
+ return "forceLocalOnSensitive is configured but no local runtime is configured, so the setting cannot keep sensitive prompts local.";
2941
+ case "soak-evidence-rejected":
2942
+ return "Graph soak evidence contained rejected observation lines; cutover gate evidence may be incomplete.";
2943
+ case "telemetry-warning":
2944
+ return "An optional telemetry backend warning occurred; local JSONL telemetry remains the fallback when enabled.";
2945
+ case "telemetry-write-failed":
2946
+ return "Writing an openteam telemetry event failed; plugin execution continued.";
2947
+ case "unknown-role":
2948
+ return "An unknown role ID was encountered; openteam used a neutral frontier-eligible fallback. If intentional, define the role in orchestrator.roles.";
2949
+ }
2950
+ }
2951
+
2881
2952
  // src/telemetry/events.ts
2882
2953
  var EVENT_SCHEMA_VERSION = 1;
2883
2954
  var EventBaseSchema = z11.object({
@@ -2971,6 +3042,13 @@ var ShadowDiagnosticEventSchema = EventBaseSchema.extend({
2971
3042
  "unknown"
2972
3043
  ])
2973
3044
  });
3045
+ var DiagnosticLevelSchema = z11.enum(["debug", "info", "warn", "error"]);
3046
+ var DiagnosticCodeSchema = z11.enum(DIAGNOSTIC_CODES);
3047
+ var DiagnosticEventSchema = EventBaseSchema.extend({
3048
+ type: z11.literal("diagnostic"),
3049
+ level: DiagnosticLevelSchema,
3050
+ code: DiagnosticCodeSchema
3051
+ });
2974
3052
  var OpenTeamEventSchema = z11.discriminatedUnion("type", [
2975
3053
  RouteEventSchema,
2976
3054
  MessageEventSchema,
@@ -2979,7 +3057,8 @@ var OpenTeamEventSchema = z11.discriminatedUnion("type", [
2979
3057
  DecisionEventSchema,
2980
3058
  ActivityEventSchema,
2981
3059
  SessionEndpointEventSchema,
2982
- ShadowDiagnosticEventSchema
3060
+ ShadowDiagnosticEventSchema,
3061
+ DiagnosticEventSchema
2983
3062
  ]);
2984
3063
 
2985
3064
  // src/telemetry/types.ts
@@ -3162,6 +3241,9 @@ async function readRouteCostRecords(dir, deps) {
3162
3241
  }
3163
3242
  return records;
3164
3243
  }
3244
+ async function readDiagnosticEvents(dir, deps) {
3245
+ return (await readSessionEvents(dir, deps)).filter((event) => event.type === "diagnostic");
3246
+ }
3165
3247
  async function readSessionEvents(dir, deps) {
3166
3248
  const files = (await deps.storage.list(dir)).filter((file) => file.endsWith(".jsonl"));
3167
3249
  const perFile = await Promise.all(files.map(async (file) => {
@@ -5728,14 +5810,21 @@ function hashPrompt(prompt) {
5728
5810
 
5729
5811
  // src/orchestrator/permissions.ts
5730
5812
  var ROLE_PERMISSIONS = {
5731
- rusty: ["read", "edit", "multiFileEdit", "destructive", "network", "shell"],
5732
- livingston: ["read", "edit", "multiFileEdit", "network", "shell"],
5733
- yen: ["read", "edit", "network", "shell"],
5734
- basher: ["read", "edit"],
5813
+ architect: [
5814
+ "read",
5815
+ "edit",
5816
+ "multiFileEdit",
5817
+ "destructive",
5818
+ "network",
5819
+ "shell"
5820
+ ],
5821
+ integration: ["read", "edit", "multiFileEdit", "network", "shell"],
5822
+ "local-runtime": ["read", "edit", "network", "shell"],
5823
+ "routing-cost": ["read", "edit"],
5735
5824
  scribe: ["read", "edit"],
5736
5825
  ralph: ["read", "edit", "shell"],
5737
5826
  guardian: ["read"],
5738
- linus: ["read", "edit", "shell"],
5827
+ tester: ["read", "edit", "shell"],
5739
5828
  reviewer: ["read"]
5740
5829
  };
5741
5830
  var elevatedTier = {
@@ -6031,6 +6120,8 @@ async function runSubsession(client, req, deps = {}) {
6031
6120
 
6032
6121
  // src/orchestrator/coordinator.ts
6033
6122
  function warnUnknownRole(roleID, warn, warnedRoles) {
6123
+ if (isKnownNonWorkerRole(roleID))
6124
+ return;
6034
6125
  if (warnedRoles !== undefined) {
6035
6126
  if (warnedRoles.has(roleID))
6036
6127
  return;
@@ -6109,7 +6200,7 @@ function routingInput(input, role) {
6109
6200
  ...input.task,
6110
6201
  prompt: input.task.prompt ?? input.prompt
6111
6202
  }).tier;
6112
- const permissionTier = elevateTierForPermissions(classifiedTier, permissionsFor(input.roleID));
6203
+ const permissionTier = elevateTierForPermissions(classifiedTier, permissionsFor(role.roleID));
6113
6204
  const tier = permissionTier;
6114
6205
  const requirement = roleToRequirement(role, input.task, tier);
6115
6206
  const task = roleAdjustedTask(role, input, requirement);
@@ -7641,6 +7732,34 @@ function agentModelLine(diagnostic, nameWidth, searchedPaths) {
7641
7732
  }
7642
7733
  return ` ${mark} ${name} [${diagnostic.mode}] ${modelText2} ${provenance} ${parts.join(" · ")}`;
7643
7734
  }
7735
+ function diagnosticMark(level) {
7736
+ switch (level) {
7737
+ case "error":
7738
+ return "✗";
7739
+ case "warn":
7740
+ return "⚠";
7741
+ default:
7742
+ return "·";
7743
+ }
7744
+ }
7745
+ function diagnosticsSection(diagnostics) {
7746
+ const notable = diagnostics.filter((diagnostic) => diagnostic.level === "warn" || diagnostic.level === "error");
7747
+ if (notable.length === 0) {
7748
+ return [];
7749
+ }
7750
+ const recent = notable.slice(-5);
7751
+ const lines = [
7752
+ ` plugin diagnostics: ${notable.length} warning/error event(s)`
7753
+ ];
7754
+ for (const diagnostic of recent) {
7755
+ lines.push(` ${diagnosticMark(diagnostic.level)} [${diagnostic.code}] ${diagnosticDescription(diagnostic.code)}`);
7756
+ }
7757
+ if (notable.length > recent.length) {
7758
+ lines.push(` … ${notable.length - recent.length} older diagnostic event(s) omitted`);
7759
+ }
7760
+ lines.push(" note: plugin diagnostics are bounded classes recorded instead of writing to the opencode TUI console.");
7761
+ return lines;
7762
+ }
7644
7763
  function agentModelsSection(diagnostics, searchedPaths) {
7645
7764
  const lines = [" agent models:"];
7646
7765
  if (diagnostics.length === 0) {
@@ -7676,6 +7795,9 @@ function renderDoctor(input) {
7676
7795
  }
7677
7796
  }
7678
7797
  lines.push(` telemetry: ${input.telemetryPath} — ${input.telemetryRecords} record(s)`);
7798
+ if (input.diagnostics !== undefined) {
7799
+ lines.push(...diagnosticsSection(input.diagnostics));
7800
+ }
7679
7801
  if (input.otelBackend !== undefined) {
7680
7802
  const otel = input.otelBackend;
7681
7803
  lines.push(" opentelemetry:");
@@ -9455,9 +9577,17 @@ async function runCli(argv, deps) {
9455
9577
  if (command === "doctor") {
9456
9578
  const config = await deps.loadConfig(configPath, configResolution);
9457
9579
  let opencodeConfigError;
9458
- const [snapshots, records, cacheEntries, agentFiles, opencodeConfig] = await Promise.all([
9580
+ const [
9581
+ snapshots,
9582
+ records,
9583
+ diagnostics,
9584
+ cacheEntries,
9585
+ agentFiles,
9586
+ opencodeConfig
9587
+ ] = await Promise.all([
9459
9588
  deps.probe(config),
9460
9589
  deps.readTelemetry(telemetryPath),
9590
+ deps.readDiagnostics !== undefined ? deps.readDiagnostics() : Promise.resolve(undefined),
9461
9591
  deps.cachePort !== undefined ? deps.cachePort.listEntries(deps.cachePort.resolveCacheRoot(parsed.cacheRoot)).catch(() => {
9462
9592
  return;
9463
9593
  }) : Promise.resolve(undefined),
@@ -9489,6 +9619,7 @@ async function runCli(argv, deps) {
9489
9619
  telemetryRecords: records.length,
9490
9620
  agentModels,
9491
9621
  opencodeConfigPaths,
9622
+ ...diagnostics !== undefined && diagnostics.length > 0 ? { diagnostics } : {},
9492
9623
  ...opencodeConfigError !== undefined ? { opencodeConfigError } : {},
9493
9624
  ...cacheEntries !== undefined ? { cacheEntries, cliVersion: deps.version } : {},
9494
9625
  ...legacyLayout !== undefined && legacyLayout.length > 0 ? { legacyLayout } : {},
@@ -9650,6 +9781,96 @@ function createCommandTool(deps) {
9650
9781
  });
9651
9782
  }
9652
9783
 
9784
+ // src/plugin/diagnostics.ts
9785
+ var PLUGIN_DIAGNOSTIC_SESSION_ID = "plugin";
9786
+ function asDiagnosticClient(client) {
9787
+ if (typeof client !== "object" || client === null) {
9788
+ return {};
9789
+ }
9790
+ return client;
9791
+ }
9792
+ function appLogOptions(diagnostic, directory) {
9793
+ return {
9794
+ body: {
9795
+ service: "openteam",
9796
+ level: diagnostic.level,
9797
+ message: diagnosticDescription(diagnostic.code),
9798
+ extra: {
9799
+ code: diagnostic.code,
9800
+ sessionID: diagnostic.sessionID,
9801
+ source: "plugin"
9802
+ }
9803
+ },
9804
+ ...directory !== undefined && directory.length > 0 ? { query: { directory } } : {}
9805
+ };
9806
+ }
9807
+ function toastOptions(diagnostic, directory) {
9808
+ return {
9809
+ body: {
9810
+ title: `openteam ${diagnostic.level}`,
9811
+ message: `${diagnosticDescription(diagnostic.code)} Run \`openteam doctor\` for details.`,
9812
+ variant: diagnostic.level === "error" ? "error" : "warning",
9813
+ duration: 8000
9814
+ },
9815
+ ...directory !== undefined && directory.length > 0 ? { query: { directory } } : {}
9816
+ };
9817
+ }
9818
+ function fireAndForget(run) {
9819
+ try {
9820
+ Promise.resolve(run()).catch(() => {});
9821
+ } catch {}
9822
+ }
9823
+ function shouldToast(diagnostic, options) {
9824
+ if (options?.toast !== undefined) {
9825
+ return options.toast;
9826
+ }
9827
+ return diagnostic.level === "warn" || diagnostic.level === "error";
9828
+ }
9829
+ function createPluginDiagnosticChannel(deps) {
9830
+ const client = asDiagnosticClient(deps.client);
9831
+ const toastKeys = new Set;
9832
+ let eventSink = deps.sink ?? createNullEventSink();
9833
+ const emit = (input, options = {}) => {
9834
+ const diagnostic = {
9835
+ v: EVENT_SCHEMA_VERSION,
9836
+ type: "diagnostic",
9837
+ ts: deps.now(),
9838
+ sessionID: input.sessionID ?? PLUGIN_DIAGNOSTIC_SESSION_ID,
9839
+ level: input.level,
9840
+ code: input.code
9841
+ };
9842
+ if (options.persist !== false) {
9843
+ fireAndForget(() => eventSink.emit(diagnostic));
9844
+ }
9845
+ const log = client.app?.log;
9846
+ if (typeof log === "function") {
9847
+ fireAndForget(() => log.call(client.app, appLogOptions(diagnostic, deps.directory)));
9848
+ }
9849
+ if (shouldToast(diagnostic, options) && !toastKeys.has(input.code)) {
9850
+ const showToast = client.tui?.showToast;
9851
+ toastKeys.add(input.code);
9852
+ if (typeof showToast === "function") {
9853
+ fireAndForget(() => showToast.call(client.tui, toastOptions(diagnostic, deps.directory)));
9854
+ }
9855
+ }
9856
+ };
9857
+ return {
9858
+ setEventSink: (sink) => {
9859
+ eventSink = sink;
9860
+ },
9861
+ emit,
9862
+ sink: (level, code, options = {}) => (message) => {
9863
+ emit({ level, code }, options);
9864
+ },
9865
+ warn: (code, options = {}) => (message) => {
9866
+ emit({ level: "warn", code }, options);
9867
+ },
9868
+ info: (code, options = {}) => (message) => {
9869
+ emit({ level: "info", code }, options);
9870
+ }
9871
+ };
9872
+ }
9873
+
9653
9874
  // src/plugin/graphTool.ts
9654
9875
  import { tool as tool2 } from "@opencode-ai/plugin";
9655
9876
 
@@ -11111,16 +11332,16 @@ function insertRecords(db, records) {
11111
11332
  throw error;
11112
11333
  }
11113
11334
  }
11114
- function warnMemoryIndexRecordRejections(result) {
11335
+ function warnMemoryIndexRecordRejections(result, warn) {
11115
11336
  if (result.rejectedRows === 0) {
11116
11337
  return;
11117
11338
  }
11118
- console.warn(`[openteam] memory index reader rejected ${result.rejectedRows} row(s); semantic memory may be incomplete.`);
11339
+ (warn ?? console.warn)(`[openteam] memory index reader rejected ${result.rejectedRows} row(s); semantic memory may be incomplete.`);
11119
11340
  for (const rejection of result.rejections) {
11120
- console.warn(`[openteam] • memory_records:${rejection.recordId}: ${rejection.detail}`);
11341
+ (warn ?? console.warn)(`[openteam] • memory_records:${rejection.recordId}: ${rejection.detail}`);
11121
11342
  }
11122
11343
  }
11123
- function createMemoryIndex(db) {
11344
+ function createMemoryIndex(db, warn) {
11124
11345
  db.exec(SCHEMA);
11125
11346
  const allRecordsFn = () => {
11126
11347
  const rows = db.query("SELECT id, payload FROM memory_records ORDER BY created_at ASC, id ASC").all();
@@ -11148,7 +11369,7 @@ function createMemoryIndex(db) {
11148
11369
  };
11149
11370
  const allRecordsWithWarning = () => {
11150
11371
  const result = allRecordsFn();
11151
- warnMemoryIndexRecordRejections(result);
11372
+ warnMemoryIndexRecordRejections(result, warn);
11152
11373
  return result.records;
11153
11374
  };
11154
11375
  const state = () => {
@@ -11987,6 +12208,7 @@ function createOrchestrateTool(deps) {
11987
12208
  sink: deps.sink,
11988
12209
  now: deps.now,
11989
12210
  newDecisionID: deps.newDecisionID,
12211
+ warn: deps.warn,
11990
12212
  warnedRoles: _pluginWarnedRoles,
11991
12213
  localRuntimeReachable: deps.localRuntimeReachable,
11992
12214
  reachableRuntimeIds: deps.reachableRuntimeIds,
@@ -12217,7 +12439,7 @@ async function buildMemoryRuntimeFromConfig(config, deps, options = {}) {
12217
12439
  const warn = deps.warn ?? console.warn;
12218
12440
  try {
12219
12441
  const db = await openDatabase(semantic.indexPath);
12220
- const index = createMemoryIndex(db);
12442
+ const index = createMemoryIndex(db, warn);
12221
12443
  const rebuildResult = await rebuildMemoryIndexFromStorage(index, semantic.logPath, { storage: deps.storage });
12222
12444
  if (rebuildResult.rejectedLines > 0) {
12223
12445
  warn(`[openteam] memory log rejected ${rebuildResult.rejectedLines} line(s); semantic memory may be incomplete.`);
@@ -12282,7 +12504,7 @@ var createOtlpSpanExporter = (connection, config) => {
12282
12504
  // package.json
12283
12505
  var package_default = {
12284
12506
  name: "@jmanuelcorral/openteam",
12285
- version: "0.9.0",
12507
+ version: "0.9.1",
12286
12508
  packageManager: "bun@1.3.14",
12287
12509
  description: "Cost-aware, local-first routing plugin for opencode with cheapest-capable frontier fallback and multi-agent orchestration.",
12288
12510
  license: "MIT",
@@ -12395,24 +12617,24 @@ function createShellExec($) {
12395
12617
  };
12396
12618
  };
12397
12619
  }
12398
- function logAvailabilityRefreshError(error) {
12620
+ function logAvailabilityRefreshError(error, warn) {
12399
12621
  const message = error instanceof Error ? error.message : String(error);
12400
- console.warn(`[openteam] availability refresh failed: ${message}`);
12622
+ (warn ?? console.warn)(`[openteam] availability refresh failed: ${message}`);
12401
12623
  }
12402
- function logTelemetryError(error) {
12624
+ function logTelemetryError(error, warn) {
12403
12625
  const message = error instanceof Error ? error.message : String(error);
12404
- console.warn(`[openteam] telemetry write failed: ${message}`);
12626
+ (warn ?? console.warn)(`[openteam] telemetry write failed: ${message}`);
12405
12627
  }
12406
- function logTelemetryWarning(message) {
12407
- console.warn(message);
12628
+ function logTelemetryWarning(message, warn) {
12629
+ (warn ?? console.warn)(message);
12408
12630
  }
12409
12631
  var opencodeServerUrlLogged = false;
12410
- function logOpencodeServerUrl(serverUrl) {
12632
+ function logOpencodeServerUrl(serverUrl, warn) {
12411
12633
  if (opencodeServerUrlLogged) {
12412
12634
  return;
12413
12635
  }
12414
12636
  opencodeServerUrlLogged = true;
12415
- console.warn(`[openteam] plugin initialisation ctx.serverUrl: ${serverUrl.toString()}`);
12637
+ (warn ?? console.warn)(`[openteam] plugin initialisation ctx.serverUrl: ${serverUrl.toString()}`);
12416
12638
  }
12417
12639
  var MAX_LOGGED_OPENCODE_VERSION_FAILURE_CAUSES = 64;
12418
12640
  function createOpencodeVersionReadFailureLogger(maxCauses = MAX_LOGGED_OPENCODE_VERSION_FAILURE_CAUSES, warn) {
@@ -12434,26 +12656,25 @@ function createOpencodeVersionReadFailureLogger(maxCauses = MAX_LOGGED_OPENCODE_
12434
12656
  (warn ?? console.warn)(`[openteam] opencode version read failed: ${message}`);
12435
12657
  };
12436
12658
  }
12437
- var logOpencodeVersionReadFailure = createOpencodeVersionReadFailureLogger();
12438
- function warnNoRuntimeConfigFound(paths, optionsOnly, localModel) {
12659
+ function warnNoRuntimeConfigFound(paths, optionsOnly, localModel, warn) {
12439
12660
  const searched = paths.join(", ");
12440
12661
  const modelRef = localModel !== null ? `\`${localModel.providerID}/${localModel.modelID}\`` : "none (no local runtime configured)";
12441
12662
  if (optionsOnly) {
12442
- console.warn(`[openteam] config file not found (searched: ${searched}); ` + `routing is configured by plugin options only — local runtimes declared ` + `in ${paths[0] ?? searched} are not loaded. ` + `Local model in use: ${modelRef}. ` + `Create ${DEFAULT_CONFIG_PATH} to persist your local runtime configuration.`);
12663
+ (warn ?? console.warn)(`[openteam] config file not found (searched: ${searched}); ` + `routing is configured by plugin options only — local runtimes declared ` + `in ${paths[0] ?? searched} are not loaded. ` + `Local model in use: ${modelRef}. ` + `Create ${DEFAULT_CONFIG_PATH} to persist your local runtime configuration.`);
12443
12664
  } else {
12444
- console.warn(`[openteam] no config file found at ${searched}. ` + `Routing uses schema defaults — local model: ${modelRef}. ` + `Privacy-sensitive tasks under forceLocalOnSensitive will be routed to frontier ` + `(no local runtime configured). Run \`openteam setup\` or create ${DEFAULT_CONFIG_PATH}.`);
12665
+ (warn ?? console.warn)(`[openteam] no config file found at ${searched}. ` + `Routing uses schema defaults — local model: ${modelRef}. ` + `Privacy-sensitive tasks under forceLocalOnSensitive will be routed to frontier ` + `(no local runtime configured). Run \`openteam setup\` or create ${DEFAULT_CONFIG_PATH}.`);
12445
12666
  }
12446
12667
  }
12447
12668
  function warnInertPrivacySetting(paths, warn) {
12448
12669
  (warn ?? console.warn)(`[openteam] privacyMode is "forceLocalOnSensitive" but no local runtime is configured ` + `(searched: ${paths.join(", ")}). ` + `Privacy-sensitive tasks will be routed to frontier and labelled "frontier" in telemetry ` + `(rationale: "privacy-force-local-none-configured"). ` + `To keep sensitive prompts local, configure a local runtime in ` + `${paths[0] ?? DEFAULT_CONFIG_PATH} or use the alwaysLocal override per request.`);
12449
12670
  }
12450
- function warnSoakEvidenceRejections(result) {
12671
+ function warnSoakEvidenceRejections(result, warn) {
12451
12672
  if (result.rejectedLines === 0) {
12452
12673
  return;
12453
12674
  }
12454
- console.warn(`[openteam] soak evidence reader rejected ${result.rejectedLines} observation line(s); cutover gate evidence may be incomplete.`);
12675
+ (warn ?? console.warn)(`[openteam] soak evidence reader rejected ${result.rejectedLines} observation line(s); cutover gate evidence may be incomplete.`);
12455
12676
  for (const rejection of result.rejections) {
12456
- console.warn(`[openteam] • ${rejection.file}:${rejection.lineNumber}: ${rejection.detail}`);
12677
+ (warn ?? console.warn)(`[openteam] • ${rejection.file}:${rejection.lineNumber}: ${rejection.detail}`);
12457
12678
  }
12458
12679
  }
12459
12680
  var SHADOW_CERT_PATH = "artifacts/graph-shadow-certificate.json";
@@ -12630,7 +12851,7 @@ function createLazyOpencodeVersionReader(serverUrl, options = {}) {
12630
12851
  }
12631
12852
  };
12632
12853
  }
12633
- async function buildGateInput(config, storage, opencodeVersion) {
12854
+ async function buildGateInput(config, storage, opencodeVersion, warn) {
12634
12855
  const shadow = await readAndParseCertificate(storage, SHADOW_CERT_PATH, (raw) => parseShadowCertificate(raw, opencodeVersion), (cert) => shadowCertificateBindingDigest(cert.evidence));
12635
12856
  const shadowDigest = shadow.status === "valid" ? shadow.digest : "0".repeat(64);
12636
12857
  const release = await readAndParseCertificate(storage, RELEASE_CERT_PATH, (raw) => parseReleaseCertificate(raw, opencodeVersion, shadowDigest), (cert) => cert.digest);
@@ -12638,7 +12859,7 @@ async function buildGateInput(config, storage, opencodeVersion) {
12638
12859
  try {
12639
12860
  const recorderPorts = recorderPortsFromStorage(storage);
12640
12861
  const chainRead = await readAllChains(recorderPorts, SOAK_EVIDENCE_DIR);
12641
- warnSoakEvidenceRejections(chainRead);
12862
+ warnSoakEvidenceRejections(chainRead, warn);
12642
12863
  if (chainRead.chains.length > 0) {
12643
12864
  soakLedgerDigest = canonicalLedger(chainRead.chains);
12644
12865
  }
@@ -12667,8 +12888,8 @@ function recorderPortsFromStorage(storage) {
12667
12888
  id: { randomUUID: () => crypto.randomUUID() }
12668
12889
  };
12669
12890
  }
12670
- async function buildMemoryInjectorFromConfig(config, storage) {
12671
- const runtime = await buildMemoryRuntimeFromConfig(config, { storage }, { requireInjectionEnabled: true });
12891
+ async function buildMemoryInjectorFromConfig(config, storage, warn) {
12892
+ const runtime = await buildMemoryRuntimeFromConfig(config, { storage, ...warn !== undefined ? { warn } : {} }, { requireInjectionEnabled: true });
12672
12893
  if (runtime === undefined) {
12673
12894
  return;
12674
12895
  }
@@ -12685,8 +12906,10 @@ async function buildMemoryInjectorFromConfig(config, storage) {
12685
12906
  }
12686
12907
  });
12687
12908
  }
12688
- function createEventSink(rawOptions, storage = createFsStorageProvider(process.cwd())) {
12909
+ function createEventSink(rawOptions, storage = createFsStorageProvider(process.cwd()), loggers = {}) {
12689
12910
  const options = telemetryOptions(rawOptions);
12911
+ const onError = loggers.onError !== undefined ? loggers.onError : (error) => logTelemetryError(error);
12912
+ const onWarn = loggers.onWarn !== undefined ? loggers.onWarn : (message) => logTelemetryWarning(message);
12690
12913
  const { sink } = createTelemetryEventSink({
12691
12914
  enabled: options.enabled,
12692
12915
  otel: OtelBackendConfigSchema.parse({
@@ -12697,13 +12920,13 @@ function createEventSink(rawOptions, storage = createFsStorageProvider(process.c
12697
12920
  eventLog: {
12698
12921
  dir: options.sessionsDir,
12699
12922
  storage,
12700
- onError: logTelemetryError
12923
+ onError
12701
12924
  },
12702
12925
  otel: {
12703
12926
  env: (name) => process.env[name],
12704
12927
  buildExporter: createOtlpSpanExporter,
12705
- onError: logTelemetryError,
12706
- onWarn: logTelemetryWarning
12928
+ onError,
12929
+ onWarn
12707
12930
  }
12708
12931
  });
12709
12932
  return sink;
@@ -12783,6 +13006,9 @@ function createCliDeps(config, registry, telemetryPath, sessionsDir = DEFAULT_SE
12783
13006
  storage,
12784
13007
  legacyTelemetryPath: path4
12785
13008
  }),
13009
+ readDiagnostics: () => readDiagnosticEvents(sessionsDir, {
13010
+ storage
13011
+ }),
12786
13012
  readGraphSnapshot: createGraphStatusSnapshotReader({
12787
13013
  storage,
12788
13014
  fallbackConfig: config,
@@ -12876,7 +13102,13 @@ function createLoopTool(deps) {
12876
13102
  });
12877
13103
  }
12878
13104
  var server = async (ctx, rawOptions) => {
12879
- logOpencodeServerUrl(ctx.serverUrl);
13105
+ const now = () => Date.now();
13106
+ const diagnostics = createPluginDiagnosticChannel({
13107
+ client: ctx.client,
13108
+ directory: ctx.directory,
13109
+ now
13110
+ });
13111
+ logOpencodeServerUrl(ctx.serverUrl, diagnostics.info("opencode-server-url", { persist: false, toast: false }));
12880
13112
  const storage = createFsStorageProvider(ctx.directory || ctx.worktree || process.cwd());
12881
13113
  const runtimeConfig = await loadRuntimeOpenTeamConfig(storage, rawOptions);
12882
13114
  const config = runtimeConfig.config;
@@ -12888,13 +13120,23 @@ var server = async (ctx, rawOptions) => {
12888
13120
  const cache = createAvailabilityCache({
12889
13121
  config,
12890
13122
  registry,
12891
- onRefreshError: logAvailabilityRefreshError
13123
+ onRefreshError: (error) => logAvailabilityRefreshError(error, diagnostics.warn("availability-refresh-failed", { toast: false }))
12892
13124
  });
12893
13125
  cache.refresh();
12894
13126
  const telemetryPath = telemetryOptions(rawOptions).path;
12895
13127
  const sessionsDir = telemetryOptions(rawOptions).sessionsDir;
12896
- const sink = createEventSink(rawOptions, storage);
12897
- const injectMemory = await buildMemoryInjectorFromConfig(config, storage);
13128
+ const sink = createEventSink(rawOptions, storage, {
13129
+ onError: (error) => logTelemetryError(error, diagnostics.warn("telemetry-write-failed", {
13130
+ persist: false,
13131
+ toast: false
13132
+ })),
13133
+ onWarn: (message) => logTelemetryWarning(message, diagnostics.warn("telemetry-warning", {
13134
+ persist: false,
13135
+ toast: false
13136
+ }))
13137
+ });
13138
+ diagnostics.setEventSink(sink);
13139
+ const injectMemory = await buildMemoryInjectorFromConfig(config, storage, diagnostics.warn("memory-runtime-warning", { toast: false }));
12898
13140
  const sessionTracker = createSessionTracker();
12899
13141
  const hooks = createHooks(config, cache.get, injectMemory === undefined ? {
12900
13142
  sink,
@@ -12906,7 +13148,6 @@ var server = async (ctx, rawOptions) => {
12906
13148
  });
12907
13149
  const cliDeps = createCliDeps(config, registry, telemetryPath, sessionsDir, storage, runtimeConfig.source, rawOptions);
12908
13150
  const toolcalls = createToolcallTracker({ sink });
12909
- const now = () => Date.now();
12910
13151
  const announcedSessions = new Set;
12911
13152
  const announceEndpoint = (sessionID) => {
12912
13153
  if (sessionID === undefined || announcedSessions.has(sessionID)) {
@@ -12923,24 +13164,25 @@ var server = async (ctx, rawOptions) => {
12923
13164
  };
12924
13165
  let graphConfig = resolveGraphConfig(config, runtimeConfig.source);
12925
13166
  if (!runtimeConfig.fileFound) {
12926
- warnNoRuntimeConfigFound(runtimeConfig.searchedPaths, runtimeConfig.optionsFound, localDefault(config));
13167
+ warnNoRuntimeConfigFound(runtimeConfig.searchedPaths, runtimeConfig.optionsFound, localDefault(config), diagnostics.warn("config-missing"));
12927
13168
  }
12928
13169
  if (config.privacyMode === "forceLocalOnSensitive" && localDefault(config) === null) {
12929
- warnInertPrivacySetting(runtimeConfig.searchedPaths);
13170
+ warnInertPrivacySetting(runtimeConfig.searchedPaths, diagnostics.warn("privacy-inert"));
12930
13171
  }
12931
13172
  const getOpencodeVersion = createLazyOpencodeVersionReader(ctx.serverUrl, {
12932
13173
  client: ctx.client,
12933
- onFailure: logOpencodeVersionReadFailure
13174
+ onFailure: createOpencodeVersionReadFailureLogger(MAX_LOGGED_OPENCODE_VERSION_FAILURE_CAUSES, diagnostics.warn("opencode-version-read-failed", { toast: false }))
12934
13175
  });
12935
13176
  if (isGateApplicable(graphConfig.effectiveMode)) {
12936
13177
  const opencodeVersion = await getOpencodeVersion();
12937
- const gateInput = await buildGateInput(config, storage, opencodeVersion);
13178
+ const gateInput = await buildGateInput(config, storage, opencodeVersion, diagnostics.warn("soak-evidence-rejected", { toast: false }));
12938
13179
  const gateResult = evaluateGraphGate(gateInput);
12939
13180
  graphConfig = applyGraphGate(graphConfig, gateResult);
12940
13181
  if (graphConfig.gateDenied === true && graphConfig.gateViolations !== undefined) {
12941
- console.warn("[openteam] ⛔ cutover gate denied active mode — degrading to off");
13182
+ const graphGateWarn = diagnostics.warn("graph-gate-denied");
13183
+ graphGateWarn("[openteam] ⛔ cutover gate denied active mode — degrading to off");
12942
13184
  for (const v of graphConfig.gateViolations) {
12943
- console.warn(`[openteam] • ${v.code}: ${v.detail}`);
13185
+ graphGateWarn(`[openteam] • ${v.code}: ${v.detail}`);
12944
13186
  }
12945
13187
  }
12946
13188
  }
@@ -13002,14 +13244,16 @@ var server = async (ctx, rawOptions) => {
13002
13244
  observeLegacyExecution: graphSurface.observeLegacyExecution,
13003
13245
  localRuntimeReachable,
13004
13246
  reachableRuntimeIds: probeReachableRuntimeIds,
13005
- runtimeLimiter
13247
+ runtimeLimiter,
13248
+ warn: diagnostics.warn("unknown-role")
13006
13249
  });
13007
13250
  const memoryTool = createMemoryTool({
13008
13251
  config,
13009
13252
  storage,
13010
13253
  now,
13011
13254
  localRuntimeReachable,
13012
- fetch: globalThis.fetch
13255
+ fetch: globalThis.fetch,
13256
+ warn: diagnostics.warn("memory-extraction-warning", { toast: false })
13013
13257
  });
13014
13258
  const loopTool = createLoopTool({
13015
13259
  storage,
@@ -13019,6 +13263,7 @@ var server = async (ctx, rawOptions) => {
13019
13263
  sink,
13020
13264
  now,
13021
13265
  newDecisionID: () => crypto.randomUUID(),
13266
+ warn: diagnostics.warn("unknown-role"),
13022
13267
  localRuntimeReachable,
13023
13268
  reachableRuntimeIds: probeReachableRuntimeIds,
13024
13269
  runtimeLimiter
@@ -13075,7 +13320,6 @@ export {
13075
13320
  readOpencodeVersion,
13076
13321
  logTelemetryWarning,
13077
13322
  logTelemetryError,
13078
- logOpencodeVersionReadFailure,
13079
13323
  logOpencodeServerUrl,
13080
13324
  logAvailabilityRefreshError,
13081
13325
  isMissingFile,