dsh-codex-subscription 2.2.2 → 2.2.3

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/lib/index.js CHANGED
@@ -369,6 +369,54 @@ const PREFERENCE_FIELDS = Object.freeze({
369
369
  }
370
370
  });
371
371
  //#endregion
372
+ //#region src/diagnostic-events.js
373
+ const areas = /* @__PURE__ */ new Set([
374
+ "login",
375
+ "model",
376
+ "catalog",
377
+ "quota",
378
+ "quota-reset",
379
+ "search",
380
+ "image"
381
+ ]);
382
+ const codes$1 = /* @__PURE__ */ new Set([
383
+ "timeout",
384
+ "dns",
385
+ "tls",
386
+ "connection",
387
+ "network",
388
+ "http-error"
389
+ ]);
390
+ /** Process-local bounded evidence. No bodies, URLs, exception strings or identities. */
391
+ function createDiagnosticEvents({ limit = 32, now = Date.now } = {}) {
392
+ const events = [];
393
+ const capacity = Math.max(1, Math.min(128, Number.isInteger(limit) ? limit : 32));
394
+ let dropped = 0, sequence = 0;
395
+ return {
396
+ record(area, value) {
397
+ if (!areas.has(area) || !["ok", "failed"].includes(value?.status)) return;
398
+ events.push({
399
+ sequence: ++sequence,
400
+ area,
401
+ status: value.status,
402
+ observedAt: now(),
403
+ ...codes$1.has(value.code) ? { code: value.code } : {},
404
+ ...Number.isInteger(value.httpStatus) && value.httpStatus >= 100 && value.httpStatus <= 599 ? { httpStatus: value.httpStatus } : {}
405
+ });
406
+ if (events.length > capacity) {
407
+ events.shift();
408
+ dropped++;
409
+ }
410
+ },
411
+ snapshot: () => ({
412
+ scope: "plugin-process",
413
+ capacity,
414
+ dropped,
415
+ events: events.map((value) => ({ ...value }))
416
+ })
417
+ };
418
+ }
419
+ //#endregion
372
420
  //#region src/oauth-network.js
373
421
  const execFileAsync = promisify(execFile);
374
422
  const CODEX_AUTH_HOST = "auth.openai.com";
@@ -612,6 +660,11 @@ const elapsedBucket = (elapsed) => elapsed < 1e3 ? "under-1s" : elapsed < 5e3 ?
612
660
  function createCodexNetworkTransport(options = {}) {
613
661
  const attempts = /* @__PURE__ */ new Map();
614
662
  const now = options.now ?? Date.now;
663
+ const evidence = createDiagnosticEvents({ now });
664
+ const remember = (area, value) => {
665
+ attempts.set(area, value);
666
+ evidence.record(area, value);
667
+ };
615
668
  const run = async (area, operation, connection = {}) => {
616
669
  const startedAt = now();
617
670
  let route = attempts.get(area)?.route ?? "direct";
@@ -625,7 +678,7 @@ function createCodexNetworkTransport(options = {}) {
625
678
  routed = true;
626
679
  }
627
680
  });
628
- if (value instanceof Response && !value.ok) attempts.set(area, {
681
+ if (value instanceof Response && !value.ok) remember(area, {
629
682
  status: "failed",
630
683
  stage: "http",
631
684
  code: "http-error",
@@ -633,14 +686,14 @@ function createCodexNetworkTransport(options = {}) {
633
686
  route,
634
687
  elapsed: elapsedBucket(now() - startedAt)
635
688
  });
636
- else if (routed || value instanceof Response) attempts.set(area, {
689
+ else if (routed || value instanceof Response) remember(area, {
637
690
  status: "ok",
638
691
  route,
639
692
  elapsed: elapsedBucket(now() - startedAt)
640
693
  });
641
694
  return value;
642
695
  } catch (error) {
643
- if (routed) attempts.set(area, {
696
+ if (routed) remember(area, {
644
697
  status: "failed",
645
698
  stage: "transport",
646
699
  code: classifyTransportError(error),
@@ -652,6 +705,7 @@ function createCodexNetworkTransport(options = {}) {
652
705
  };
653
706
  return Object.freeze({
654
707
  run,
708
+ history: evidence.snapshot,
655
709
  fetch: (area, input, init) => run(area, () => globalThis.fetch(input, init)),
656
710
  snapshot: () => Object.fromEntries([...attempts].map(([area, value]) => [area, { ...value }]))
657
711
  });
@@ -1316,6 +1370,21 @@ function createSubagentBackendSwitcher({ entries, prepare, persist }) {
1316
1370
  backgroundMode: "one-shot",
1317
1371
  maxDepth: "provider-managed"
1318
1372
  });
1373
+ const ownedFields = [
1374
+ "provider",
1375
+ "modelSelectionSettings",
1376
+ "backgroundMode",
1377
+ "maxDepth"
1378
+ ];
1379
+ const restore = (current, original) => {
1380
+ const result = { ...current }, applied = convert(original);
1381
+ for (const key of ownedFields) {
1382
+ if (current[key] !== applied[key]) continue;
1383
+ if (Object.hasOwn(original, key)) result[key] = original[key];
1384
+ else delete result[key];
1385
+ }
1386
+ return result;
1387
+ };
1319
1388
  const configure = (fiber, config) => {
1320
1389
  if (!standard(fiber.entry, config)) return config;
1321
1390
  if (!originals.has(fiber)) originals.set(fiber, { ...config });
@@ -1345,7 +1414,8 @@ function createSubagentBackendSwitcher({ entries, prepare, persist }) {
1345
1414
  fiber,
1346
1415
  before
1347
1416
  });
1348
- await fiber.update(mode === "codex" ? convert(original) : original, true);
1417
+ if (mode === "codex" && before.provider !== "codex-subscription-subagent") originals.set(fiber, { ...before });
1418
+ await fiber.update(mode === "codex" ? convert(before) : restore(before, original), true);
1349
1419
  }
1350
1420
  await persist?.(mode);
1351
1421
  } catch (error) {
@@ -1364,7 +1434,7 @@ function createSubagentBackendSwitcher({ entries, prepare, persist }) {
1364
1434
  await tail.catch(() => {});
1365
1435
  disposed = true;
1366
1436
  selected = "dsh";
1367
- for (const [fiber, original] of originals) if (fiber.config?.provider === "codex-subscription-subagent") await fiber.update(original, true);
1437
+ for (const [fiber, original] of originals) if (fiber.config?.provider === "codex-subscription-subagent") await fiber.update(restore(fiber.config, original), true);
1368
1438
  originals.clear();
1369
1439
  }
1370
1440
  };
@@ -2860,8 +2930,28 @@ function openaiCodexSubscriptionProvider({ resolveSpeedMode = () => void 0, reso
2860
2930
  }
2861
2931
  Object.freeze(["0.82.1", "0.85.1"]);
2862
2932
  //#endregion
2933
+ //#region src/subscription-credentials.js
2934
+ async function readSubscriptionCredentials(getAuth, readCredential, signal) {
2935
+ for (let attempt = 0; attempt < 2; attempt++) {
2936
+ const auth = await getAuth({ signal });
2937
+ signal?.throwIfAborted();
2938
+ if (typeof auth?.auth?.apiKey !== "string" || !auth.auth.apiKey) break;
2939
+ const credential = await readCredential({ signal });
2940
+ signal?.throwIfAborted();
2941
+ if (credential?.type !== "oauth") break;
2942
+ if (credential.access === auth.auth.apiKey) return {
2943
+ access: credential.access,
2944
+ accountId: credential.accountId
2945
+ };
2946
+ }
2947
+ return {
2948
+ access: void 0,
2949
+ accountId: void 0
2950
+ };
2951
+ }
2952
+ //#endregion
2863
2953
  //#region src/version.js
2864
- const PACKAGE_VERSION = "2.2.2";
2954
+ const PACKAGE_VERSION = "2.2.3";
2865
2955
  const USER_AGENT = `dsh-codex-subscription/${PACKAGE_VERSION}`;
2866
2956
  //#endregion
2867
2957
  //#region src/model-catalog.js
@@ -2986,12 +3076,8 @@ function createOfficialModelCatalog(options = {}) {
2986
3076
  let timer;
2987
3077
  const timeoutError = /* @__PURE__ */ new Error("Codex model catalog refresh timed out");
2988
3078
  const work = (async () => {
2989
- const auth = await options.getAuth({ signal: requestSignal });
3079
+ const { access, accountId } = await readSubscriptionCredentials(options.getAuth, options.readCredential, requestSignal);
2990
3080
  if (currentGeneration !== generation || requestSignal.aborted) return false;
2991
- const credential = await options.readCredential({ signal: requestSignal });
2992
- if (currentGeneration !== generation || requestSignal.aborted) return false;
2993
- const access = auth?.auth?.apiKey;
2994
- const accountId = credential?.type === "oauth" ? credential.accountId : void 0;
2995
3081
  if (typeof access !== "string" || access.length === 0 || typeof accountId !== "string" || accountId.length === 0) return false;
2996
3082
  const headers = {
2997
3083
  authorization: `Bearer ${access}`,
@@ -3148,10 +3234,7 @@ function createCodexSearchProvider(options) {
3148
3234
  signal?.throwIfAborted();
3149
3235
  const preferences = readCapabilitySettings(options.resolvePreferences?.());
3150
3236
  if (preferences.searchMode === "disabled") throw new WebError("Codex search is disabled in subscription settings", "WEB_PROVIDER_UNAVAILABLE");
3151
- const auth = await options.getAuth({ signal });
3152
- const credential = await options.readCredential({ signal });
3153
- const access = auth?.auth?.apiKey;
3154
- const accountId = credential?.type === "oauth" ? credential.accountId : void 0;
3237
+ const { access, accountId } = await readSubscriptionCredentials(options.getAuth, options.readCredential, signal);
3155
3238
  if (typeof access !== "string" || access.length === 0 || typeof accountId !== "string" || accountId.length === 0) throw new WebError("ChatGPT subscription is not signed in", "WEB_PROVIDER_CREDENTIAL_MISSING");
3156
3239
  const model = nonEmpty$1(options.resolveModel?.()) ?? DEFAULT_MODEL;
3157
3240
  const id = nonEmpty$1(options.resolveSessionId?.()) ?? randomUUID();
@@ -3662,10 +3745,7 @@ function createCodexImageTool(options) {
3662
3745
  const prompt = nonEmpty(args.prompt);
3663
3746
  if (prompt === void 0) throw new Error("prompt must be a non-empty string");
3664
3747
  const imageOptions = normalizeImageOptions(args);
3665
- const auth = await options.getAuth({ signal: exec.signal });
3666
- const credential = await options.readCredential({ signal: exec.signal });
3667
- const access = auth?.auth?.apiKey;
3668
- const accountId = credential?.type === "oauth" ? credential.accountId : void 0;
3748
+ const { access, accountId } = await readSubscriptionCredentials(options.getAuth, options.readCredential, exec.signal);
3669
3749
  if (typeof access !== "string" || access.length === 0 || typeof accountId !== "string" || accountId.length === 0) throw new Error("ChatGPT subscription is not signed in");
3670
3750
  if (!attachments.imageLimits.mediaTypes.includes("image/png")) throw new Error("This DSH installation does not accept PNG image attachments");
3671
3751
  const maximumBytes = Math.min(attachments.imageLimits.maxImageBytes, attachments.imageLimits.maxMessageImageBytes);
@@ -3907,6 +3987,229 @@ var OriginalImageStore = class {
3907
3987
  }
3908
3988
  };
3909
3989
  //#endregion
3990
+ //#region src/diagnostic-checks.js
3991
+ const states = /* @__PURE__ */ new Set([
3992
+ "pass",
3993
+ "fail",
3994
+ "warn",
3995
+ "unknown",
3996
+ "not-applicable"
3997
+ ]);
3998
+ /** Adapters return bounded reason codes, never exceptions or raw service data. */
3999
+ async function collectChecks(definitions, { timeoutMs = 1500, now = Date.now, source = "server", scope = "plugin-process" } = {}) {
4000
+ return Promise.all(definitions.map(async ({ id, capability, run }) => {
4001
+ const startedAt = now();
4002
+ let timer;
4003
+ const controller = new AbortController();
4004
+ const base = {
4005
+ id,
4006
+ capability,
4007
+ source,
4008
+ scope
4009
+ };
4010
+ try {
4011
+ if (!run) return {
4012
+ ...base,
4013
+ status: "unknown",
4014
+ reason: "not-instrumented",
4015
+ observedAt: now()
4016
+ };
4017
+ const result = await Promise.race([Promise.resolve().then(() => run(controller.signal)), new Promise((resolve) => {
4018
+ timer = setTimeout(() => {
4019
+ controller.abort();
4020
+ resolve({
4021
+ status: "unknown",
4022
+ reason: "inspection-timeout"
4023
+ });
4024
+ }, timeoutMs);
4025
+ })]);
4026
+ const valid = states.has(result?.status) && /^[a-z][a-z0-9-]{0,63}$/u.test(result?.reason ?? "");
4027
+ return {
4028
+ ...base,
4029
+ status: valid ? result.status : "unknown",
4030
+ reason: valid ? result.reason : "invalid-inspection-result",
4031
+ observedAt: now(),
4032
+ elapsedMs: Math.max(0, now() - startedAt)
4033
+ };
4034
+ } catch {
4035
+ return {
4036
+ ...base,
4037
+ status: "unknown",
4038
+ reason: "inspection-failed",
4039
+ observedAt: now(),
4040
+ elapsedMs: Math.max(0, now() - startedAt)
4041
+ };
4042
+ } finally {
4043
+ clearTimeout(timer);
4044
+ }
4045
+ }));
4046
+ }
4047
+ const diagnosticCapabilities = Object.freeze([
4048
+ "account",
4049
+ "accounts",
4050
+ "settings",
4051
+ "models",
4052
+ "quota",
4053
+ "search",
4054
+ "images",
4055
+ "sketch",
4056
+ "subagents",
4057
+ "compaction",
4058
+ "transport",
4059
+ "storage",
4060
+ "host-ui"
4061
+ ]);
4062
+ //#endregion
4063
+ //#region src/diagnostic-capabilities.js
4064
+ function capabilityCoverage({ account, preference, runtime, catalog, tools, storage, checks, operations, now = Date.now() }) {
4065
+ const disabled = {
4066
+ images: preference.imageGeneration === false && preference.imageEditing === false,
4067
+ sketch: preference.imageSketch === false || preference.imageEditing === false,
4068
+ search: preference.searchMode === "disabled",
4069
+ compaction: preference.compactionMode === "dsh",
4070
+ subagents: preference.subagentBackend === "dsh"
4071
+ };
4072
+ return diagnosticCapabilities.map((id) => {
4073
+ let readiness = "unknown", reason = "not-instrumented", blockedBy;
4074
+ if (disabled[id]) {
4075
+ readiness = "not-applicable";
4076
+ reason = "disabled-or-host-owned";
4077
+ } else if ([
4078
+ "models",
4079
+ "quota",
4080
+ "search",
4081
+ "images",
4082
+ "compaction"
4083
+ ].includes(id) && account.status === "signed-out") {
4084
+ readiness = "warn";
4085
+ reason = "sign-in-required";
4086
+ blockedBy = "account";
4087
+ } else if (id === "account") {
4088
+ readiness = account.status === "signed-in" ? "pass" : "unknown";
4089
+ reason = account.status === "signed-in" ? "local-sign-in-present" : "sign-in-unverified";
4090
+ } else if (id === "settings" && typeof preference.writable === "boolean") {
4091
+ readiness = preference.writable ? "pass" : "warn";
4092
+ reason = preference.writable ? "settings-writable" : "settings-readonly";
4093
+ } else if (id === "subagents" && runtime) {
4094
+ readiness = runtime.installed && !runtime.restartRequired ? "pass" : "warn";
4095
+ reason = runtime.restartRequired ? "restart-required" : runtime.installed ? "runtime-present" : "runtime-missing";
4096
+ } else if (id === "models" && catalog) {
4097
+ readiness = catalog.source === "online" ? "pass" : "warn";
4098
+ reason = catalog.source === "online" ? "catalog-online" : "catalog-fallback";
4099
+ } else if (id === "storage" && storage?.readable) {
4100
+ readiness = "pass";
4101
+ reason = "storage-readable";
4102
+ } else if (tools && (id === "images" || id === "sketch" && preference.imageSketchAgent === true)) {
4103
+ readiness = tools[id] ? "pass" : "warn";
4104
+ reason = tools[id] ? "tool-registered" : "tool-missing";
4105
+ }
4106
+ const recent = (operations?.events ?? []).filter((event) => event.capability === id && now >= event.observedAt && now - event.observedAt <= 15 * 6e4).at(-1);
4107
+ return {
4108
+ id,
4109
+ readiness,
4110
+ reason,
4111
+ ...blockedBy ? { blockedBy } : {},
4112
+ checks: checks.filter((check) => check.capability === id).map((check) => check.id),
4113
+ execution: recent ? "observed" : "not-verified",
4114
+ ...recent ? { latest: {
4115
+ action: recent.action,
4116
+ source: recent.source,
4117
+ status: recent.status,
4118
+ observedAt: recent.observedAt
4119
+ } } : {}
4120
+ };
4121
+ });
4122
+ }
4123
+ //#endregion
4124
+ //#region src/diagnostic-operations.js
4125
+ const actions = Object.freeze({
4126
+ "login/start": "account",
4127
+ "login/submit": "account",
4128
+ "login/cancel": "account",
4129
+ logout: "account",
4130
+ "account/select": "accounts",
4131
+ "account/remove": "accounts",
4132
+ "preferences/update": "settings",
4133
+ "account/cleanup": "accounts",
4134
+ usage: "quota",
4135
+ "preferences/models": "models",
4136
+ "storage/status": "storage",
4137
+ "storage/clear-forecast": "storage",
4138
+ "runtime/install": "subagents",
4139
+ "runtime/remove": "subagents",
4140
+ "runtime/cancel": "subagents",
4141
+ codex_image_generate: "images",
4142
+ codex_sketch: "sketch"
4143
+ });
4144
+ function safeOperations(raw) {
4145
+ if (!Array.isArray(raw?.events)) return void 0;
4146
+ return {
4147
+ scope: "plugin-process",
4148
+ capacity: 32,
4149
+ dropped: (Number.isSafeInteger(raw.dropped) && raw.dropped >= 0 ? raw.dropped : 0) + Math.max(0, raw.events.length - 32),
4150
+ events: raw.events.slice(-32).flatMap((value) => {
4151
+ if (!Object.hasOwn(actions, value?.action ?? "") || ![
4152
+ "completed",
4153
+ "failed",
4154
+ "cancelled"
4155
+ ].includes(value.status) || !["rpc", "tool"].includes(value.source) || !Number.isSafeInteger(value.observedAt) || value.observedAt < 0) return [];
4156
+ return [{
4157
+ capability: actions[value.action],
4158
+ action: value.action,
4159
+ status: value.status,
4160
+ source: value.source,
4161
+ observedAt: value.observedAt
4162
+ }];
4163
+ })
4164
+ };
4165
+ }
4166
+ function createDiagnosticOperations({ now = Date.now } = {}) {
4167
+ const events = [];
4168
+ let sequence = 0, dropped = 0;
4169
+ const record = (action, status, source = "rpc") => {
4170
+ if (!Object.hasOwn(actions, action) || ![
4171
+ "completed",
4172
+ "failed",
4173
+ "cancelled"
4174
+ ].includes(status) || !["rpc", "tool"].includes(source)) return;
4175
+ events.push({
4176
+ sequence: ++sequence,
4177
+ capability: actions[action],
4178
+ action,
4179
+ source,
4180
+ status,
4181
+ observedAt: now()
4182
+ });
4183
+ if (events.length > 32) {
4184
+ events.shift();
4185
+ dropped++;
4186
+ }
4187
+ };
4188
+ return {
4189
+ record,
4190
+ wrap: (handler) => async (endpoint, payload, signal) => {
4191
+ try {
4192
+ const result = await handler(endpoint, payload, signal);
4193
+ record(endpoint, result?.ok === true ? "completed" : "failed");
4194
+ return result;
4195
+ } catch (error) {
4196
+ record(endpoint, signal?.aborted ? "cancelled" : "failed");
4197
+ throw error;
4198
+ }
4199
+ },
4200
+ tool: (exec, result) => {
4201
+ if (!["codex_image_generate", "codex_sketch"].includes(exec?.name)) return;
4202
+ record(exec.name, exec.signal?.aborted ? "cancelled" : result?.isError === true ? "failed" : result?.isError === false ? "completed" : "failed", "tool");
4203
+ },
4204
+ snapshot: () => ({
4205
+ scope: "plugin-process",
4206
+ capacity: 32,
4207
+ dropped,
4208
+ events: events.map((value) => ({ ...value }))
4209
+ })
4210
+ };
4211
+ }
4212
+ //#endregion
3910
4213
  //#region src/diagnostics.js
3911
4214
  const requestAreas = /* @__PURE__ */ new Set([
3912
4215
  "login",
@@ -3939,6 +4242,26 @@ const elapsedBuckets = /* @__PURE__ */ new Set([
3939
4242
  "5-15s",
3940
4243
  "over-15s"
3941
4244
  ]);
4245
+ function safeHistory(raw) {
4246
+ if (!raw || !Array.isArray(raw.events)) return void 0;
4247
+ const events = raw.events.slice(-32).flatMap((value) => {
4248
+ if (!value || !requestAreas.has(value.area) || !statuses.has(value.status) || !Number.isSafeInteger(value.sequence) || value.sequence < 1 || !Number.isSafeInteger(value.observedAt) || value.observedAt < 0) return [];
4249
+ return [{
4250
+ area: value.area,
4251
+ status: value.status,
4252
+ sequence: value.sequence,
4253
+ observedAt: value.observedAt,
4254
+ ...codes.has(value.code) ? { code: value.code } : {},
4255
+ ...Number.isInteger(value.httpStatus) && value.httpStatus >= 100 && value.httpStatus <= 599 ? { httpStatus: value.httpStatus } : {}
4256
+ }];
4257
+ });
4258
+ return {
4259
+ scope: "plugin-process",
4260
+ capacity: 32,
4261
+ dropped: (Number.isSafeInteger(raw.dropped) && raw.dropped >= 0 ? raw.dropped : 0) + Math.max(0, raw.events.length - 32),
4262
+ events
4263
+ };
4264
+ }
3942
4265
  function safeRequests(network) {
3943
4266
  const raw = network?.snapshot?.() ?? {};
3944
4267
  const result = {};
@@ -3957,17 +4280,113 @@ function safeRequests(network) {
3957
4280
  return result;
3958
4281
  }
3959
4282
  /** Build a support report that deliberately excludes OAuth and account metadata. */
3960
- async function createSubscriptionDiagnostics({ auth, preferences, login = { phase: "idle" }, network, modelCatalog, connection, compaction }) {
4283
+ async function createSubscriptionDiagnostics({ auth, preferences, login = { phase: "idle" }, network, modelCatalog, connection, compaction, inspectionOptions, operations, runtimeManagement, storage, tools }) {
4284
+ const collected = {};
4285
+ const checks = await collectChecks([
4286
+ [
4287
+ "account",
4288
+ "account",
4289
+ () => auth.status()
4290
+ ],
4291
+ [
4292
+ "preferences",
4293
+ "settings",
4294
+ () => preferences.status()
4295
+ ],
4296
+ [
4297
+ "catalog",
4298
+ "models",
4299
+ () => modelCatalog?.status?.()
4300
+ ],
4301
+ [
4302
+ "gaps",
4303
+ "models",
4304
+ () => modelCatalog?.capabilityGaps?.()
4305
+ ],
4306
+ [
4307
+ "requests",
4308
+ "transport",
4309
+ () => network?.snapshot ? safeRequests(network) : void 0
4310
+ ],
4311
+ [
4312
+ "history",
4313
+ "transport",
4314
+ () => safeHistory(network?.history?.())
4315
+ ],
4316
+ [
4317
+ "websocket",
4318
+ "transport",
4319
+ () => connection ? safeCounters(connection, [
4320
+ "requests",
4321
+ "connectionsCreated",
4322
+ "connectionsReused",
4323
+ "deltaRequests",
4324
+ "websocketFailures",
4325
+ "sseFallbacks"
4326
+ ]) : void 0
4327
+ ],
4328
+ [
4329
+ "compaction",
4330
+ "compaction",
4331
+ () => compaction ? safeCounters(compaction, [
4332
+ "requests",
4333
+ "checkpointsSaved",
4334
+ "checkpointsReused"
4335
+ ]) : void 0
4336
+ ],
4337
+ [
4338
+ "operations",
4339
+ "transport",
4340
+ () => safeOperations(operations?.snapshot?.())
4341
+ ],
4342
+ [
4343
+ "runtime",
4344
+ "subagents",
4345
+ async () => {
4346
+ const value = await runtimeManagement?.status?.();
4347
+ return value ? Object.fromEntries([
4348
+ "available",
4349
+ "installed",
4350
+ "restartRequired"
4351
+ ].filter((key) => typeof value[key] === "boolean").map((key) => [key, value[key]])) : void 0;
4352
+ }
4353
+ ],
4354
+ [
4355
+ "storage",
4356
+ "storage",
4357
+ async () => typeof storage === "function" ? (await storage(), { readable: true }) : void 0
4358
+ ],
4359
+ [
4360
+ "tools",
4361
+ "host-ui",
4362
+ () => typeof tools?.get === "function" ? {
4363
+ images: !!tools.get("codex_image_generate"),
4364
+ sketch: !!tools.get("codex_sketch")
4365
+ } : void 0
4366
+ ]
4367
+ ].map(([id, capability, read]) => ({
4368
+ id,
4369
+ capability,
4370
+ run: read ? async (signal) => {
4371
+ const value = await read();
4372
+ if (value === void 0) return {
4373
+ status: "unknown",
4374
+ reason: "not-instrumented"
4375
+ };
4376
+ if (!signal.aborted) collected[id] = value;
4377
+ return {
4378
+ status: "pass",
4379
+ reason: "inspection-completed"
4380
+ };
4381
+ } : void 0
4382
+ })), inspectionOptions);
3961
4383
  let account = { status: "unknown" };
3962
4384
  const issues = [];
3963
- try {
3964
- account = { status: (await auth.status()).authenticated === true ? "signed-in" : "signed-out" };
3965
- } catch {
3966
- issues.push({ code: "account-status-unavailable" });
3967
- }
3968
- const preference = preferences.status();
3969
- const catalog = modelCatalog?.status?.();
3970
- const gaps = (modelCatalog?.capabilityGaps?.() ?? []).slice(0, 20).flatMap((value) => {
4385
+ if (collected.account) account = { status: collected.account.authenticated === true ? "signed-in" : "signed-out" };
4386
+ else issues.push({ code: "account-status-unavailable" });
4387
+ const preference = collected.preferences ?? {};
4388
+ const catalog = collected.catalog;
4389
+ const gaps = (Array.isArray(collected.gaps) ? collected.gaps : []).slice(0, 20).flatMap((value) => {
3971
4390
  if (!value || typeof value.model !== "string" || !/^[a-z][a-z0-9._-]{0,79}$/u.test(value.model)) return [];
3972
4391
  const fields = Object.fromEntries([
3973
4392
  "reasoning",
@@ -3985,6 +4404,22 @@ async function createSubscriptionDiagnostics({ auth, preferences, login = { phas
3985
4404
  if (gaps.length) issues.push({ code: "catalog-capabilities-not-adapted" });
3986
4405
  return {
3987
4406
  schemaVersion: 3,
4407
+ generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
4408
+ inspection: {
4409
+ schemaVersion: 1,
4410
+ meaning: "collection-success-is-not-feature-success",
4411
+ checks,
4412
+ capabilities: capabilityCoverage({
4413
+ account,
4414
+ preference,
4415
+ runtime: collected.runtime,
4416
+ catalog,
4417
+ tools: collected.tools,
4418
+ storage: collected.storage,
4419
+ checks,
4420
+ operations: collected.operations
4421
+ })
4422
+ },
3988
4423
  package: "dsh-codex-subscription",
3989
4424
  version: PACKAGE_VERSION,
3990
4425
  runtime: {
@@ -3994,20 +4429,11 @@ async function createSubscriptionDiagnostics({ auth, preferences, login = { phas
3994
4429
  },
3995
4430
  account,
3996
4431
  login,
3997
- requests: safeRequests(network),
3998
- ...connection ? { websocket: safeCounters(connection, [
3999
- "requests",
4000
- "connectionsCreated",
4001
- "connectionsReused",
4002
- "deltaRequests",
4003
- "websocketFailures",
4004
- "sseFallbacks"
4005
- ]) } : {},
4006
- ...compaction ? { compaction: safeCounters(compaction, [
4007
- "requests",
4008
- "checkpointsSaved",
4009
- "checkpointsReused"
4010
- ]) } : {},
4432
+ requests: collected.requests ?? {},
4433
+ ...collected.history ? { requestHistory: collected.history } : {},
4434
+ ...collected.operations ? { operations: collected.operations } : {},
4435
+ ...collected.websocket ? { websocket: collected.websocket } : {},
4436
+ ...collected.compaction ? { compaction: collected.compaction } : {},
4011
4437
  ...catalog && ["fallback", "online"].includes(catalog.source) && [
4012
4438
  "idle",
4013
4439
  "refreshing",
@@ -4025,7 +4451,7 @@ async function createSubscriptionDiagnostics({ auth, preferences, login = { phas
4025
4451
  ...typeof preference.outputVerbosity === "string" ? { outputVerbosity: preference.outputVerbosity } : {},
4026
4452
  searchProvider: preference.searchProvider,
4027
4453
  speedMode: preference.speedMode,
4028
- writable: preference.writable === true
4454
+ ...typeof preference.writable === "boolean" ? { writable: preference.writable } : {}
4029
4455
  },
4030
4456
  issues
4031
4457
  };
@@ -4192,10 +4618,7 @@ function createCodexUsageReader(options) {
4192
4618
  let inFlight;
4193
4619
  let generation = 0;
4194
4620
  const load = async (signal) => {
4195
- const auth = await getAuth({ signal });
4196
- const credential = await readCredential({ signal });
4197
- const access = auth?.auth?.apiKey;
4198
- const accountId = credential?.type === "oauth" ? credential.accountId : void 0;
4621
+ const { access, accountId } = await readSubscriptionCredentials(getAuth, readCredential, signal);
4199
4622
  if (typeof access !== "string" || access.length === 0 || typeof accountId !== "string" || accountId.length === 0) throw new Error("ChatGPT subscription is not signed in");
4200
4623
  const response = await fetchUsage(CODEX_USAGE_URL, {
4201
4624
  method: "GET",
@@ -4851,9 +5274,7 @@ const requestSignal = (signal, timeoutMs) => {
4851
5274
  function safeCopy(value) {
4852
5275
  return typeof value === "string" && value.length > 0 ? value.slice(0, MAX_COPY_LENGTH) : void 0;
4853
5276
  }
4854
- function credentialsOf(auth, credential) {
4855
- const access = auth?.auth?.apiKey;
4856
- const accountId = credential?.type === "oauth" ? credential.accountId : void 0;
5277
+ function credentialsOf({ access, accountId }) {
4857
5278
  if (typeof access !== "string" || access.length === 0 || typeof accountId !== "string" || accountId.length === 0) throw new Error("ChatGPT subscription is not signed in");
4858
5279
  return {
4859
5280
  access,
@@ -4938,7 +5359,7 @@ function createCodexResetCreditService(options) {
4938
5359
  ...credit.title === void 0 ? {} : { name: credit.title },
4939
5360
  ...credit.creditExpiresAt === void 0 ? {} : { expiresAt: credit.creditExpiresAt }
4940
5361
  });
4941
- const resolveCredentials = async (signal) => credentialsOf(await getAuth({ signal }), await readCredential({ signal }));
5362
+ const resolveCredentials = async (signal) => credentialsOf(await readSubscriptionCredentials(getAuth, readCredential, signal));
4942
5363
  const readDetails = async (signal, credentials) => {
4943
5364
  const { access, accountId } = credentials ?? await resolveCredentials(signal);
4944
5365
  const response = await fetchReset(CODEX_RESET_CREDITS_URL, {
@@ -5129,7 +5550,7 @@ const publicError = (code, message) => ({
5129
5550
  details: { issues: [] }
5130
5551
  }
5131
5552
  });
5132
- function createSubscriptionRpcHandler({ authHandler, usageReader, resetCreditService, preferences, runtimeManagement, diagnosticsReader, modelCatalog, originalImages, resolveInheritedOriginal, closeConnections, onAccountChanged }) {
5553
+ function createSubscriptionRpcHandler({ authHandler, usageReader, resetCreditService, preferences, runtimeManagement, diagnosticsReader, modelCatalog, originalImages, resolveInheritedOriginal, closeConnections, onAccountChanged, onCleanupFailure }) {
5133
5554
  return async (endpoint, payload, signal) => {
5134
5555
  if (endpoint === "storage/status" || endpoint === "storage/clear-forecast") try {
5135
5556
  signal.throwIfAborted();
@@ -5287,23 +5708,20 @@ function createSubscriptionRpcHandler({ authHandler, usageReader, resetCreditSer
5287
5708
  return publicError("internal", message);
5288
5709
  }
5289
5710
  const result = await authHandler(endpoint, payload, signal);
5290
- if (result.ok === true && [
5291
- "logout",
5292
- "account/select",
5293
- "account/remove"
5294
- ].includes(endpoint)) closeConnections?.();
5295
- if (endpoint === "account/remove" && result.ok === true && typeof payload?.id === "string") await usageReader.clearScope(payload.id);
5296
- if (endpoint === "logout" && result.ok === true) {
5297
- await usageReader.clear();
5298
- resetCreditService.clear();
5299
- modelCatalog?.clear();
5300
- onAccountChanged?.();
5301
- } else if (result.ok === true && (endpoint === "account/select" || endpoint === "account/remove" || endpoint === "login/status" && result.value?.authenticated === true)) {
5302
- usageReader.clearCache();
5303
- resetCreditService.clear();
5304
- modelCatalog?.clear();
5305
- onAccountChanged?.();
5306
- modelCatalog?.refresh({ signal: void 0 }).catch(() => {});
5711
+ if (result.ok === true && (endpoint === "logout" || endpoint === "account/select" || endpoint === "account/remove" || endpoint === "login/status" && result.value?.authenticated === true)) {
5712
+ const tasks = [
5713
+ () => closeConnections?.(),
5714
+ () => usageReader.clearCache(),
5715
+ () => resetCreditService.clear(),
5716
+ () => modelCatalog?.clear(),
5717
+ () => onAccountChanged?.()
5718
+ ];
5719
+ if (endpoint === "logout") tasks.push(() => usageReader.clear());
5720
+ if (endpoint === "account/remove" && typeof payload?.id === "string") tasks.push(() => usageReader.clearScope(payload.id));
5721
+ if ((await Promise.allSettled(tasks.map((task) => Promise.resolve().then(task)))).some((value) => value.status === "rejected")) try {
5722
+ onCleanupFailure?.();
5723
+ } catch {}
5724
+ if (endpoint !== "logout") Promise.resolve().then(() => modelCatalog?.refresh({ signal: void 0 })).catch(() => {});
5307
5725
  } else if (result.ok === true && (endpoint === "status" || result.value?.authenticated === true)) modelCatalog?.refresh({ signal: void 0 }).catch(() => {});
5308
5726
  return result;
5309
5727
  };
@@ -5381,6 +5799,8 @@ function apply(ctx, config = {}) {
5381
5799
  const settings = createSettingsAdapter(ctx, z.object(settingsFields), config, SETTINGS_NAMESPACE);
5382
5800
  const searchProvider = createSearchProviderSwitcher(ctx.loader);
5383
5801
  const network = createCodexNetworkTransport();
5802
+ const diagnosticOperations = createDiagnosticOperations();
5803
+ ctx.effect(() => ctx.on?.("tools/result", (exec, result) => diagnosticOperations.tool(exec, result)), "codex-subscription: operation evidence");
5384
5804
  const originalImages = new OriginalImageStore();
5385
5805
  const accountVault = ACCOUNT_VAULT_KEY !== void 0 && typeof ctx.credentials.readRecord === "function" && typeof ctx.credentials.modifyRecord === "function" && typeof ctx.credentials.deleteRecord === "function" ? new DshOAuthAccountVault(ctx.credentials, {
5386
5806
  key: ACCOUNT_VAULT_KEY,
@@ -5714,14 +6134,19 @@ function apply(ctx, config = {}) {
5714
6134
  network,
5715
6135
  modelCatalog,
5716
6136
  connection,
5717
- compaction
6137
+ compaction,
6138
+ operations: diagnosticOperations,
6139
+ runtimeManagement,
6140
+ storage: () => usageReader.storage(),
6141
+ tools: ctx.tools
5718
6142
  }),
6143
+ onCleanupFailure: () => diagnosticOperations.record("account/cleanup", "failed"),
5719
6144
  modelCatalog,
5720
6145
  closeConnections: () => connection.dispose(),
5721
6146
  originalImages,
5722
6147
  resolveInheritedOriginal: (sessionId, assetId) => inheritedOriginalImageRef(ctx.get?.("sessions")?.get?.(sessionId), assetId)
5723
6148
  });
5724
- const handler = (endpoint, payload, signal) => endpoint.startsWith("sketch/") ? sketchBridge.rpc(endpoint, payload) : subscriptionHandler(endpoint, payload, signal);
6149
+ const handler = diagnosticOperations.wrap((endpoint, payload, signal) => endpoint.startsWith("sketch/") ? sketchBridge.rpc(endpoint, payload) : subscriptionHandler(endpoint, payload, signal));
5725
6150
  ctx.effect(() => {
5726
6151
  modelCatalog.refresh().catch((error) => ctx.logger?.debug?.("could not refresh Codex model catalog: %s", error.message));
5727
6152
  }, "codex-subscription: official model catalog");