Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion server/lib/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ The barrel `server/lib/index.js` is a machine-checkable enumeration of every pub
| `quotaBurnConfig.js` | Quota-burn plan shape: the provider families, the burn-job type alphabet + catalog the config page renders its form from, `QUOTA_BURN_BOUNDS` (the one bounds table the normalizer clamps to, the Zod schemas reject against, and the catalog descriptors publish as min/max), and total normalization (`normalizeQuotaBurnConfig`). Owns the dispatch-cap sentinel too (`QUOTA_BURN_UNLIMITED_DISPATCHES` / `isUnlimitedDispatchCap`) — -1 means the window is not counted, and is the default. Also owns the queued burn task's description shape (`burnTaskDescription` / `quotaBurnFamilyOfDescription`), shared with migration 225, and the `run once` vocabulary (`quotaBurnJobKey` / `jobIsSpent`, plus the two family predicates `familyIsConfigured` and `familyHasRunnableJobs`). Pure — no storage, no provider I/O. |
| `quotaBurnPresets.js` | `QUOTA_BURN_PROMPT_PRESETS` — ready-made single-focus audit prompts for `agent-prompt` burn jobs (UX, a11y, mobile, failure paths, perf, test gaps, dead code, data safety, docs, security), each filing GitHub issues and changing no code. Templates: picking one COPIES its prompt into the job, so editing them never rewrites a configured job. `findQuotaBurnPreset(id)`. |
| `auditCatalog.js` | Shared catalog of scheduled AUDIT task types (`AUDIT_DEFINITIONS` / `AUDIT_TASK_TYPES`) that can either implement a fix or file tracker issues. `isFileIssuesMode`, `auditDoWorkRequiresWorktree`, `getAuditFilingPreset`, `modeContractFor`, `applyAuditModeWrapper`. Each quota-burn audit preset maps to a scheduled type here (guarded by `auditCatalog.test.js`). Pure — no I/O. |
| `quotaBurnTaskRef.js` | The quota-burn STEP model: a discriminated scheduled-task reference (`builtin` task type + validated target app, or `custom` app-job id whose own scope is authoritative) plus the per-invocation `overrides` bag (provider/model/effort/params — unset inherits the task's saved settings, and nothing written here edits the schedule). `normalizeQuotaBurnTaskRef` / `normalizeQuotaBurnOverrides` / `normalizeQuotaBurnParams`, the `QUOTA_BURN_UNAVAILABLE` reason codes, `resolveQuotaBurnStepAvailability` (catalog passed IN, so this stays pure) / `applyQuotaBurnAvailability`, and `quotaBurnStepIsDispatchable`. |
| `quotaBurnValidation.js` | Zod schemas for the Quota Burn routes (partial config PUT, manual-run body, `run once` re-arm body). |
| `quotaReset.js` | `parseHumanReset` turns a provider CLI's human reset string into ISO 8601 (call it from the adapter); `normalizeResetAt`/`hoursUntilReset` compute time remaining without treating unknown values as imminent resets; `parseObservedReset` pulls the reset a provider stated in its own refusal text, and `isObservedBlockActive` is the shared "is that refusal still holding?" predicate every observed-refusal ledger uses. |
| `quotaWindows.js` | Classifies a quota window by PERIOD (`windowPeriodHours`); `classifyWindows` splits one card's windows into the weekly allowance that expires unused and the 5-hour one that refuses first, in one pass. `windowLabelOf` names a window. Pure. |
Expand Down Expand Up @@ -181,7 +182,7 @@ The barrel `server/lib/index.js` is a machine-checkable enumeration of every pub
| `subscriptionSavings.js` | Subscription-vs-API savings math for the usage page — `resolveSavingsWindow` (clamps an open-ended report range to today / first activity day), `prorateMonthlyCost` (monthly plan price → this window's share, `DAYS_PER_MONTH`, capped by `MAX_MONTHLY_COST`), `savingsPercent` / `costMultiplier` (null, never 0, when the comparison is undefined), `attributeReportCostToFamilies` (groups report rows by their stamped `family`), `roundCents` (the one money rounder), and `buildSubscriptionSavings({ entries, range, unmatchedApiCost })` → per-family rows + totals. Pure. |
| `credentialRegistry.js` | Pure catalog of PortOS credentials (`CREDENTIALS`, `CREDENTIAL_IDS`, `CREDENTIAL_TIERS`) — one entry per key/token an install can use (`id`, `label`, `unlocks`, `tier`, `getUrl`, `envVars`, `settingsPath`, `configurePath`, optional `feature`). Sits beside `instanceFeatureRegistry.js` so the two lists stay greppable together. Runtime resolution (settings / repo `.env` / inherited `process.env` / CLI / instance config) lives in `services/credentialInventory.js`. The Settings > Credentials page never receives a value or masked prefix. |
| `instanceFeatureRegistry.js` | The registry of optional per-install features (`INSTANCE_FEATURES`, `INSTANCE_FEATURE_IDS`, `APP_FEATURE_IDS`) — pure data, so `validation.js` derives its feature schemas from it and `navManifest.js` can be checked against it without a service→lib inversion. Runtime resolution (stored override → auto-detection → `defaultEnabled`) lives in `services/instanceFeatures.js`. A feature id tagged on a nav entry hides that page from ⌘K and the sidebar when the feature is off. |
| `providerFamilies.js` | Subscription-quota FAMILY identity — `PROVIDER_FAMILIES` (`{ id, label, matches }` for claude/codex/agy/grok), `PROVIDER_FAMILY_IDS`, `familyLabel`, `familyForProvider(config)` → family id or null (local-runtime wrappers and API-only providers belong to none). The pure half of the registry `services/providerUsage.js` attaches quota `fetch`ers to, so cost attribution and route validation can ask "which plan is this provider on?" without importing the PTY-scrape graph. Distinct from `providerVendors.js`, which is argv-shaped and includes vendors with no subscription quota. |
| `providerFamilies.js` | Subscription-quota FAMILY identity — `PROVIDER_FAMILIES` (`{ id, label, idPattern, matches }` for claude/codex/agy/grok), `PROVIDER_FAMILY_IDS`, `familyLabel`, `familyForProvider(config)`, `familyForProviderId(id)` (the weaker id-only classifier a Zod schema can use, null when the id names no family) → family id or null (local-runtime wrappers and API-only providers belong to none). The pure half of the registry `services/providerUsage.js` attaches quota `fetch`ers to, so cost attribution and route validation can ask "which plan is this provider on?" without importing the PTY-scrape graph. Distinct from `providerVendors.js`, which is argv-shaped and includes vendors with no subscription quota. |
| `fleetQuotas.js` | Unifies subscription-quota readings across federated instances — one plan, several machines, each able to read only its own local CLI. `sanitizeQuotaCards` bounds a peer-supplied payload to the wire shape; `mergeFleetQuotaCards(localCards, peerEntries)` folds every peer's reading into this install's cards, taking the FRESHEST reading per limit key (the meters are account-wide, so summing them would multiply one allowance) and SUMMING activity counts (those are per-machine, which is why the provider captions them "does not include other devices"); `fleetNote` writes the caption naming what was combined. `metrics[]` is left local — its values are prose, not addends. Fed by `services/providerQuotaShare.js` (this machine's readings, persisted) and `services/peerUsage.js` (the `usage` sync category that carries them). |
| `harnessOutput.js` | Parsers for what a coding-agent HARNESS prints about itself: `parseHarnessVersion(stdout)` (the one semver run in a `--version` banner, `null` when unparseable), `compareHarnessVersions(a, b)` (the null-guarding wrapper around `versionUtils.js#compareSemver` — `null` when either side is unparseable, so a version that did not parse never reads as "out of date"), `parseHarnessModels(harnessId, stdout)` + `HARNESS_MODEL_PARSER_IDS` (OpenCode's `provider/model` lines and Grok's bulleted list are parsed here; Antigravity and Cursor DELEGATE to `antigravity.js#parseAntigravityModelList` / `aiToolkit/internal/cursor.js#parseCursorModelList`, which the provider-card refresh has used for far longer), `MAX_MODELS`, and `parseNpmLatestVersion`. Pure: the service layer runs the child and hands the captured stdout here, so the vendor output shapes are pinned by table-driven tests instead of by running six real binaries in CI. Model ids come back in the exact spelling `--model` takes — namespaces kept where the vendor keeps them. Consumed by `services/providerRuntimeInstaller.js` and `services/harnesses.js`. |
| `providerGateways.js` | `PROVIDER_GATEWAYS` — one row per hosted OpenAI-compatible gateway an OpenCode CLI/TUI wrapper can front-end (`orcarouter`, `openrouter`), plus `PROVIDER_GATEWAY_IDS`, `gatewayById`, `isGatewayNamespace(ns)` and `gatewayForProvider(config)` → row or null. Each row's `id` is simultaneously the OpenCode provider namespace, the `gatewayBacked` marker value, and the id of the sibling `api` record that owns the key — so the sibling lookup is `providers[gateway.id]` and an OrcaRouter key can never satisfy an OpenRouter wrapper. Replaces the `orcarouterBacked` boolean + literal `'orcarouter'` that had been hand-copied across ~15 server and client files (namespace resolution, the OpenCode config builder, both zod schemas, the model-fetcher table, the sibling-key attach, the prerequisite check, and the two "not a local runtime" carve-outs in `cliChildEnv.js`/`localProviderRuntime.js`). Reads the legacy per-gateway boolean FOREVER, so stored records are never rewritten. Distinct from a local runtime (`ollamaBacked`, `vllmBacked`, …): remote, always authenticating, and no thinking toggle. Deliberately mirrored in `aiToolkit/internal/gateways.js` (the vendored toolkit may not import out) and `client/src/utils/providerGateways.js` (the browser cannot import server code) — `providerGateways.parity.test.js` fails when the first two drift. Dependency-light: imports nothing. |
Expand Down Expand Up @@ -381,6 +382,7 @@ pm` default, `NPM_CONFIG_PREFIX`, nvm/Volta) installed `codex` successfully and
| `taskRequeue.js` | The REQUEUE stamp (#3376) — pure metadata helpers for the one BACKWARD lifecycle step (`in_progress → pending`, performed by the orphan sweep and the retry-hold release). `REQUEUED_AT_KEY` / `LAST_SPAWNED_AT_KEY` name the two stamps; `isPostSpawnRequeue(pendingTask, inProgressTask)` answers whether the requeue happened strictly AFTER that spawn, which is how the federated merge tells a real requeue apart from an ordinary edit landing on a peer's stale `pending` copy. Returns false when either stamp is missing, so callers fall back to the lifecycle rank. |
| `taskRetryHold.js` | The failed-task RETRY HOLD state (#3373) — pure metadata helpers shared by the failure verdict, the post-cleanup release, the spawn guard, and the orphan sweep. `retryHoldMetadata(agentId, now)` arms it (task stays `in_progress`, so no dequeue tier can claim the retry before its resume pointer is resolved); `clearedRetryHoldMetadata()` releases it in the same write that flips the task to `pending`; `isRetryHeld(metadata)` / `isRetryHoldOwner(metadata, agentId)` gate the spawn and the owner-scoped release; `isStaleRetryHold(metadata, now, graceMs)` + `RETRY_HOLD_GRACE_MS` let the orphan sweep finish a transition whose process died. |
| `taskTargetBranch.js` | Pure task-branch contract: `resolveTaskTargetBranch(metadata)` reads a retry's legacy `existingBranch` or a review-loop follow-up's canonical `reviewLoopPRBranch`; `shouldStripTaskTargetBranch(metadata)` identifies only retry-owned pointers for terminal cleanup. |
| `taskTargetScope.js` | Target scope for a scheduled task type: `INSTALL_WIDE_TASK_TYPES`, `MANAGED_APP_TARGET_TASK_TYPES`, `PROGRAMMATIC_SCHEDULED_TASK_TYPES`, `isProgrammaticScheduledTaskType`, `requiresManagedAppTarget`, `requiresInstallWideTarget`. Declared here (not in `services/taskScheduleRegistry.js`, which re-exports it) so `quotaBurnValidation.js` can reject a burn reference with the wrong target scope without importing upward into services. |
| `taxonomyTally.js` | Generic taxonomy tally + top-N-line render engine shared by the two Layered Intelligence leaf taxonomies (`services/layeredIntelligenceRejections.js`, `layeredIntelligenceExecutionFailures.js`). `createTaxonomyTally({predicate, select, field, vocabulary, sentinel, glossFn, gapWording})` is the single composed seam — it binds a taxonomy config into `{summarize, format}`, where `summarize(records)` yields the three-bucket `{entries, unknown, unclassified, diagnosed, total}` tally (commonest-first + taxonomy-order tie-break) and `format(records, limit)` renders one prompt line naming every non-zero gap. Also exports the two leaf utilities the classifiers use directly: `normalizeToken(value)` (lowercase + separator-collapse for label/category matching) and `formatTaxonomyToken(token, labels)` (gloss-map render, nullish→'', unglossed passthrough). Pure leaf — imports nothing from the LI graph. |
| `worktreeOwnership.js` | Pure ownership gate for destructive worktree operations. `worktreeOwnershipReason()` applies root, agent-id, lock, active-agent, liveness and claim policies IN THAT ORDER, so an unconditional hold always outranks a claim and callers never re-derive the precedence from the slug; the claim hold has two opt-outs (`allowStaleClaim` for reapers, `allowLiveClaim` for the dispatch side). `worktreeAgentId()` is separator-safe; `isHumanClaimWorktree()` and `isAgentWorktreeId()` make the protected namespaces explicit; `worktreeHoldExpiresAt()` names when a hold lapses on its own (the stale-claim window) so callers can wait exactly that long. |
| `xmlEntities.js` | Shared dependency-free XML/HTML entity decoder. `decodeXmlEntities(str, extraEntities?)` — single-pass (double-decode-safe) decode of the five predefined named entities + decimal/hex numeric refs, with an optional caller-supplied extra-entity map (e.g. `{ nbsp: ' ', zwnj: '' }`). Unknown/out-of-range refs left untouched. Used by the Apple Health XML parser, Claude changelog feed, Pinterest RSS, generic feeds, and Gmail HTML-to-text. |
Expand Down
11 changes: 10 additions & 1 deletion server/lib/importScoping.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,16 @@ describe('deferred imports stay deferred (#6156)', () => {
// Deferring catalog/version parsers removes 296 instantiations (88,360 →
// 88,064). Restore the documented ~1.5k allowance for ordinary leaf growth;
// keep the negative runtime-installer guard above so eager parsing cannot return.
const MAX_STATIC_INSTANTIATIONS = 89500;
//
// #6377 measures 89,889. Its share is ~389, and it is the tolerated shape, not
// the one this budget exists to catch: two new LEAF vocabulary modules with no
// subtree behind them — `lib/taskTargetScope.js` (four constants, zero imports)
// and `lib/quotaBurnTaskRef.js` (pure shape + resolver, importing only
// `objects.js`, which every reacher already had). They are reached by the ~200
// suites that cross `lib/quotaBurnConfig.js`, so a leaf costs ~200 apiece with
// nothing to defer. Restore the ~1.5k allowance again rather than inching the
// number up by a few hundred per PR.
const MAX_STATIC_INSTANTIATIONS = 91400;

const SKIP_DIRS = new Set(['node_modules', 'coverage', 'dist', 'data']);
const serverTestFiles = (dir = SERVER_DIR, out = []) => {
Expand Down
2 changes: 2 additions & 0 deletions server/lib/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,7 @@ export * as reviewerConfig from './reviewerConfig.js';
export * from './quotaBurnConfig.js';
export * from './quotaBurnPresets.js';
export * from './auditCatalog.js';
export * from './quotaBurnTaskRef.js';
export * from './quotaBurnValidation.js';
export * from './quotaReset.js';
export * from './quotaWindows.js';
Expand Down Expand Up @@ -350,6 +351,7 @@ export * from './taskBlockCategories.js';
export * from './taskRequeue.js';
export * from './taskRetryHold.js';
export * from './taskTargetBranch.js';
export * from './taskTargetScope.js';
export * from './taxonomyTally.js';
export * from './worktreeOwnership.js';
export * from './xmlEntities.js';
Expand Down
22 changes: 22 additions & 0 deletions server/lib/providerFamilies.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,25 @@ export const PROVIDER_FAMILIES = [
{
id: 'claude',
label: 'Claude Code',
idPattern: /claude/i,
matches: (p) => (p.type === 'cli' || p.type === 'tui') && isClaudeCommand(p.command)
},
{
id: 'codex',
label: 'Codex',
idPattern: /codex/i,
matches: (p) => commandBasename(p.command) === 'codex'
},
{
id: 'agy',
label: 'Antigravity',
idPattern: /antigravity|(^|[-_])agy([-_]|$)/i,
matches: (p) => commandBasename(p.command) === 'agy' || /antigravity/i.test(p.id || '')
},
{
id: 'grok',
label: 'Grok',
idPattern: /grok/i,
matches: (p) => isGrokCommand(p.command) || /grok/i.test(p.id || '')
}
];
Expand All @@ -60,3 +64,21 @@ export function familyForProvider(provider) {
if (!provider || provider.ollamaBacked === true || provider.lmstudioBacked === true || provider.mtplxBacked === true || provider.llamaBacked === true || provider.vllmBacked === true || provider.sglangBacked === true) return null;
return PROVIDER_FAMILIES.find((f) => f.matches(provider))?.id ?? null;
}

/**
* The family a provider ID alone identifies, or `null` when the id says nothing.
*
* Strictly weaker than `familyForProvider`, and deliberately so: the BINARY is
* what really identifies a family, and a schema validating a stored config has
* only the id — the provider list is service state a Zod schema cannot reach.
* So this answers the one question that CAN be settled from an id, and answers
* `null` for everything else, letting the caller fall through to the live
* check. Used by `quotaBurnValidation.js` to reject a burn step pinned to
* another family's subscription before it reaches disk; a provider whose id
* names no family is accepted there and resolved at dispatch time instead.
*/
export function familyForProviderId(id) {
const value = typeof id === 'string' ? id.trim() : '';
if (!value) return null;
return PROVIDER_FAMILIES.find((f) => f.idPattern.test(value))?.id ?? null;
}
Loading