@cmmd-center/forge 0.9.16 → 0.9.17

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/bin.cjs CHANGED
@@ -22345,6 +22345,40 @@ const ServerProviderAuth = require_Schema$1.Struct({
22345
22345
  accountEmail: require_Schema$1.optional(TrimmedNonEmptyString),
22346
22346
  provenance: require_Schema$1.optional(ProviderAuthProvenance)
22347
22347
  });
22348
+ /**
22349
+ * Whether this Environment will let a person select the provider.
22350
+ *
22351
+ * Published for every provider the build knows about. A provider is never
22352
+ * dropped from the list: absence is not a state, and four different reasons
22353
+ * once collapsed into one "Coming soon." string because the Shell had to infer
22354
+ * meaning from what was missing. See
22355
+ * `docs/adr/withheld-providers-carry-their-reason.md`.
22356
+ */
22357
+ const ProviderAvailability = require_Schema$1.Literals(["available", "unavailable"]);
22358
+ /**
22359
+ * Why a Withheld Provider is withheld. The four members are the glossary's,
22360
+ * in `CONTEXT.md` section "Provider availability": Unbuilt, Not Offered, Not
22361
+ * Usable, Not on this Environment. Only Unbuilt may read "Coming soon."
22362
+ *
22363
+ * An Environment can only ever report Unbuilt, Not Usable, or Not on this
22364
+ * Environment. Not Offered is a CMMD fact about the organization's Provider
22365
+ * Offer, which the Shell adds when it joins.
22366
+ */
22367
+ const ProviderUnavailableReason = require_Schema$1.Literals([
22368
+ "unbuilt",
22369
+ "not-offered",
22370
+ "not-usable",
22371
+ "not-on-this-environment"
22372
+ ]);
22373
+ /**
22374
+ * The Not Usable sub-case. It picks a second line of copy, not a fifth state,
22375
+ * which is why it is a separate field rather than three more reasons.
22376
+ */
22377
+ const ProviderUnavailableDetail = require_Schema$1.Literals([
22378
+ "no-connection",
22379
+ "delivery-pending",
22380
+ "reconnect-required"
22381
+ ]);
22348
22382
  const ServerProviderModel = require_Schema$1.Struct({
22349
22383
  slug: TrimmedNonEmptyString,
22350
22384
  name: TrimmedNonEmptyString,
@@ -22376,6 +22410,9 @@ const ServerProvider = require_Schema$1.Struct({
22376
22410
  checkedAt: IsoDateTime,
22377
22411
  message: require_Schema$1.optional(TrimmedNonEmptyString),
22378
22412
  models: require_Schema$1.ArraySchema(ServerProviderModel),
22413
+ availability: require_Schema$1.optional(ProviderAvailability),
22414
+ unavailableReason: require_Schema$1.optional(ProviderUnavailableReason),
22415
+ unavailableDetail: require_Schema$1.optional(ProviderUnavailableDetail),
22379
22416
  slashCommands: require_Schema$1.ArraySchema(ServerProviderSlashCommand).pipe(require_Schema$1.withDecodingDefault(require_Schema$1.succeed([]))),
22380
22417
  skills: require_Schema$1.ArraySchema(ServerProviderSkill).pipe(require_Schema$1.withDecodingDefault(require_Schema$1.succeed([])))
22381
22418
  });
@@ -65123,7 +65160,7 @@ function normalizeNumberish(value) {
65123
65160
  }
65124
65161
  //#endregion
65125
65162
  //#region package.json
65126
- var version$1 = "0.9.16";
65163
+ var version$1 = "0.9.17";
65127
65164
  //#endregion
65128
65165
  //#region src/sentry.ts
65129
65166
  const SERVER_APP_NAME = "forge-server";
@@ -285043,14 +285080,87 @@ const writeProviderStatusCache = (input) => {
285043
285080
  })));
285044
285081
  };
285045
285082
  //#endregion
285046
- //#region src/provider/Layers/ProviderRegistry.ts
285083
+ //#region ../../packages/shared/src/providerExecution.ts
285084
+ /**
285085
+ * Canonical provider-readiness gate for starting work.
285086
+ *
285087
+ * Cloud user VMs can execute through Forge's scoped provider proxy without a
285088
+ * provider-local login. In that case the provider reports unauthenticated but
285089
+ * its credential provenance is explicitly `proxied`.
285090
+ */
285091
+ function isServerProviderExecutable(provider) {
285092
+ const hasExecutionAuthority = provider.auth.status !== "unauthenticated" || provider.auth.provenance?.credentialScope === "proxied";
285093
+ return provider.enabled && provider.installed && provider.status === "ready" && hasExecutionAuthority && provider.models.length > 0;
285094
+ }
285095
+ //#endregion
285096
+ //#region src/provider/providerAvailability.ts
285047
285097
  /**
285048
- * Providers surfaced in the UI but lacking a dedicated detection Layer.
285049
- * Every provider now has a real Layer; this array stays as a (currently
285050
- * empty) hook for any future provider that ships in the UI before its
285051
- * server-side detection lands.
285098
+ * Derive what this Environment knows about a provider's availability.
285099
+ *
285100
+ * WHY THIS EXISTS
285101
+ *
285102
+ * Four different states (Unbuilt, Not Offered, Not Usable, Not on this
285103
+ * Environment) used to render as one "Coming soon." string, because consumers
285104
+ * were handed a filtered list and had to infer meaning from what was missing.
285105
+ * Every provider is now published with an explicit availability and, when
285106
+ * withheld, its reason. See `docs/adr/withheld-providers-carry-their-reason.md`
285107
+ * and the glossary in `CONTEXT.md` section "Provider availability".
285108
+ *
285109
+ * WHAT AN ENVIRONMENT MAY SAY
285110
+ *
285111
+ * Three of the four reasons only. Not Offered is a fact about the CMMD
285112
+ * organization's Provider Offer, and delivery-pending is a fact about a
285113
+ * credential in flight; neither is visible from inside an Environment, so the
285114
+ * Shell adds them when it joins. Nothing here may invent them.
285115
+ *
285116
+ * This reads the snapshot a provider Layer already produced. It runs no probe
285117
+ * and changes no probe: `isServerProviderExecutable` stays the single gate on
285118
+ * whether a turn can be dispatched, and this restates its answer in the
285119
+ * vocabulary a person sees.
285052
285120
  */
285053
- const STUB_PROVIDERS = [];
285121
+ /**
285122
+ * Providers Forge ships in the UI but cannot execute, published as Unbuilt.
285123
+ *
285124
+ * Empty in this build: codex, claudeAgent, ollama, cmmd, apex, cursor and
285125
+ * opencode all have a real detection Layer and a registered adapter. The list
285126
+ * stays as the one place a provider that ships ahead of its server-side
285127
+ * implementation is named, so "Coming soon." is a deliberate entry rather than
285128
+ * a fallback something fell into.
285129
+ */
285130
+ const UNBUILT_PROVIDERS = [];
285131
+ function deriveProviderAvailability(provider, unbuiltProviders = UNBUILT_PROVIDERS) {
285132
+ if (unbuiltProviders.includes(provider.provider)) return {
285133
+ availability: "unavailable",
285134
+ unavailableReason: "unbuilt"
285135
+ };
285136
+ if (isServerProviderExecutable(provider)) return { availability: "available" };
285137
+ const hasProxiedCredential = provider.auth.provenance?.credentialScope === "proxied";
285138
+ if (provider.installed && provider.auth.status === "unauthenticated" && !hasProxiedCredential) return {
285139
+ availability: "unavailable",
285140
+ unavailableReason: "not-usable",
285141
+ unavailableDetail: "no-connection"
285142
+ };
285143
+ return {
285144
+ availability: "unavailable",
285145
+ unavailableReason: "not-on-this-environment"
285146
+ };
285147
+ }
285148
+ /**
285149
+ * Stamp a snapshot with its derived availability.
285150
+ *
285151
+ * The previous fields are dropped before the new ones are applied. Merging
285152
+ * would leave a provider that just connected reporting `available` while still
285153
+ * carrying the reason it was withheld for.
285154
+ */
285155
+ function withProviderAvailability(provider, unbuiltProviders = UNBUILT_PROVIDERS) {
285156
+ const { availability: _availability, unavailableReason: _unavailableReason, unavailableDetail: _unavailableDetail, ...rest } = provider;
285157
+ return {
285158
+ ...rest,
285159
+ ...deriveProviderAvailability(provider, unbuiltProviders)
285160
+ };
285161
+ }
285162
+ //#endregion
285163
+ //#region src/provider/Layers/ProviderRegistry.ts
285054
285164
  const loadProviders = (codexProvider, claudeProvider, ollamaProvider, cmmdProvider, apexProvider, cursorProvider, opencodeProvider) => require_Schema$1.all([
285055
285165
  codexProvider.getSnapshot,
285056
285166
  claudeProvider.getSnapshot,
@@ -285087,9 +285197,9 @@ const ProviderRegistryLive = require_Schema$1.effect(ProviderRegistry, require_S
285087
285197
  cachedProvider,
285088
285198
  fallbackProvider
285089
285199
  });
285090
- return STUB_PROVIDERS.includes(provider) ? fallbackProvider : void 0;
285200
+ return UNBUILT_PROVIDERS.includes(provider) ? fallbackProvider : void 0;
285091
285201
  }));
285092
- }, { concurrency: "unbounded" }).pipe(require_Schema$1.map((providers) => orderProviderSnapshots(providers.filter((provider) => provider !== void 0))));
285202
+ }, { concurrency: "unbounded" }).pipe(require_Schema$1.map((providers) => orderProviderSnapshots(providers.filter((provider) => provider !== void 0).map((provider) => withProviderAvailability(provider)))));
285093
285203
  const providersRef = yield* require_HttpServer.make$3(cachedProviders);
285094
285204
  const persistProvider = (provider) => writeProviderStatusCache({
285095
285205
  filePath: cachePathByProvider.get(provider.provider),
@@ -285098,7 +285208,8 @@ const ProviderRegistryLive = require_Schema$1.effect(ProviderRegistry, require_S
285098
285208
  provider: provider.provider,
285099
285209
  error
285100
285210
  })), require_Schema$1.ignore);
285101
- const upsertProviders = require_Schema$1.fn("upsertProviders")(function* (nextProviders, options) {
285211
+ const upsertProviders = require_Schema$1.fn("upsertProviders")(function* (incomingProviders, options) {
285212
+ const nextProviders = incomingProviders.map((provider) => withProviderAvailability(provider));
285102
285213
  const [previousProviders, providers] = yield* require_HttpServer.modify(providersRef, (previousProviders) => {
285103
285214
  const mergedProviders = new Map(previousProviders.map((provider) => [provider.provider, provider]));
285104
285215
  for (const provider of nextProviders) mergedProviders.set(provider.provider, provider);
@@ -287887,7 +287998,7 @@ function resolveBuildCommitFromEnv(env) {
287887
287998
  * environment descriptor down instead of reporting an honest "unknown".
287888
287999
  */
287889
288000
  function readBakedBuildCommit() {
287890
- return "e65edd4d1b8a361863a52b171b5e73295d691e11";
288001
+ return "86c71b55bb7a5dfbbad063d6e4c082570759f006";
287891
288002
  }
287892
288003
  async function resolveServerBuildCommit(input) {
287893
288004
  if (isFullCommitSha(input.baked)) return input.baked;
@@ -300625,21 +300736,24 @@ function processRuntimeReplicationOutboxItem(item, dependencies) {
300625
300736
  //#endregion
300626
300737
  //#region src/runtimeReplication/Services/ActiveHourMeter.ts
300627
300738
  var ActiveHourMeter = class extends require_Schema$1.Service()("forge/runtimeReplication/Services/ActiveHourMeter") {};
300628
- //#endregion
300629
- //#region ../../packages/shared/src/providerExecution.ts
300739
+ /** `slug` and `name` are `.min(1).max(512)` on the receiving schema. */
300740
+ const FIELD_MAX_LENGTH = 512;
300741
+ /** `version` is `.max(128)` on the receiving schema, with no length floor. */
300742
+ const VERSION_MAX_LENGTH = 128;
300743
+ /** `binaryPath` is `.max(2048)` on the receiving schema, with no length floor. */
300744
+ const BINARY_PATH_MAX_LENGTH = 2048;
300630
300745
  /**
300631
- * Canonical provider-readiness gate for starting work.
300746
+ * Trim to the receiving schema's maximum, or null.
300632
300747
  *
300633
- * Cloud user VMs can execute through Forge's scoped provider proxy without a
300634
- * provider-local login. In that case the provider reports unauthenticated but
300635
- * its credential provenance is explicitly `proxied`.
300748
+ * Neither field has a length floor, so an empty string would be legal — but it
300749
+ * is not an answer. A version nobody resolved and a binary nobody found are
300750
+ * both null, which is what the control plane stores and what a reader can tell
300751
+ * apart from a real value.
300636
300752
  */
300637
- function isServerProviderExecutable(provider) {
300638
- const hasExecutionAuthority = provider.auth.status !== "unauthenticated" || provider.auth.provenance?.credentialScope === "proxied";
300639
- return provider.enabled && provider.installed && provider.status === "ready" && hasExecutionAuthority && provider.models.length > 0;
300753
+ function cappedText(value, max) {
300754
+ const normalized = value?.trim().slice(0, max);
300755
+ return normalized ? normalized : null;
300640
300756
  }
300641
- /** `slug` and `name` are `.min(1).max(512)` on the receiving schema. */
300642
- const FIELD_MAX_LENGTH = 512;
300643
300757
  function serializeModel(model) {
300644
300758
  const slug = model.slug.trim().slice(0, FIELD_MAX_LENGTH);
300645
300759
  const name = model.name.trim().slice(0, FIELD_MAX_LENGTH);
@@ -300653,20 +300767,48 @@ function serializeModel(model) {
300653
300767
  name
300654
300768
  };
300655
300769
  }
300770
+ /**
300771
+ * The availability to publish for one provider.
300772
+ *
300773
+ * The registry stamps every snapshot before it leaves, so this normally copies.
300774
+ * Deriving over a stamped snapshot would lose the one fact only the registry
300775
+ * holds — which providers this build cannot run at all — and republish every
300776
+ * Unbuilt provider as Not on this Environment. The fallback covers a snapshot
300777
+ * that reached the outbox from some other path.
300778
+ *
300779
+ * Built by spread so a key with no value is ABSENT rather than `undefined`: the
300780
+ * receiving object is `.strict()`, `undefined` is not a legal enum member, and
300781
+ * the outbox retries the resulting 400 forever.
300782
+ */
300783
+ function publishedAvailability(provider) {
300784
+ if (provider.availability === void 0) return deriveProviderAvailability(provider);
300785
+ const reason = provider.unavailableReason;
300786
+ const detail = provider.unavailableDetail;
300787
+ return {
300788
+ availability: provider.availability,
300789
+ ...reason === void 0 ? {} : { unavailableReason: reason },
300790
+ ...detail === void 0 ? {} : { unavailableDetail: detail }
300791
+ };
300792
+ }
300793
+ function serializeProvider(provider) {
300794
+ const models = [];
300795
+ for (const model of provider.models) {
300796
+ if (models.length >= 256) break;
300797
+ const serialized = serializeModel(model);
300798
+ if (serialized) models.push(serialized);
300799
+ }
300800
+ return {
300801
+ provider: provider.provider,
300802
+ models,
300803
+ ...publishedAvailability(provider),
300804
+ version: cappedText(provider.version, VERSION_MAX_LENGTH),
300805
+ binaryPath: cappedText(provider.auth.provenance?.binaryPath, BINARY_PATH_MAX_LENGTH)
300806
+ };
300807
+ }
300656
300808
  function serializeProviderCatalog(providers) {
300657
300809
  const catalog = [];
300658
300810
  for (const provider of providers) {
300659
- if (!isServerProviderExecutable(provider)) continue;
300660
- const models = [];
300661
- for (const model of provider.models) {
300662
- if (models.length >= 256) break;
300663
- const serialized = serializeModel(model);
300664
- if (serialized) models.push(serialized);
300665
- }
300666
- catalog.push({
300667
- provider: provider.provider,
300668
- models
300669
- });
300811
+ catalog.push(serializeProvider(provider));
300670
300812
  if (catalog.length >= 16) break;
300671
300813
  }
300672
300814
  return catalog;