@openspecui/server 7.0.2 → 9.0.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.
Files changed (2) hide show
  1. package/dist/index.mjs +179 -70
  2. package/package.json +3 -3
package/dist/index.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { a as resolveCt2ModelDownloadPlanFromRepositoryFiles } from "./src-Dh_UAz5C.mjs";
2
2
  import { t as resolveGgufModelDownloadPlanFromRepositoryFiles } from "./src-CZfiVL_-.mjs";
3
- import { AI_TOOLS, ActiveRootMutationSchema, ActiveRootRevisionSchema, AgentDeliveryPolicyUpdateSchema, BatchTranslateInputSchema, CliExecutor, CliProjectionCommandError, CliProjectionNoticeSchema, CodeEditorThemeSchema, ConfigManager, CustomSoundHashSchema, DASHBOARD_METRIC_KEYS, DashboardConfigSchema, DashboardGitSnapshotSchema, DashboardSummaryInvalidationSchema, DashboardSummaryProjectionStateSchema, DashboardTrendsProjectionSchema, DocumentTranslationConfigUpdateSchema, EnvironmentDefaultStoreUpdateSchema, EnvironmentGlobalConfigValueSchema, EnvironmentGlobalFileProjectionDataSchema, EnvironmentGlobalFileProjectionStateSchema, EnvironmentGlobalProjectionDataSchema, EnvironmentGlobalProjectionStateSchema, GitConfigSchema, GlobalSettingsManager, LocalModelAssetStateSchema, LocalModelLifecycleFileStateSchema, LocalModelLifecycleGroupStateSchema, LocalModelProfileManifestSchema, MarkdownParser, NotificationPublishInputSchema, NotificationSettingsSchema, OPENSPECUI_DOCUMENT_HOOK_VERSION, OPENSPECUI_WORKFLOW_HOOK_VERSION, OPSX_ARTIFACT_INPUT_ACTIONS, OPSX_CHANGE_INPUT_ACTIONS, OPSX_COMMAND_CAPABLE_WORKFLOWS, OPSX_CORE_PROFILE_WORKFLOWS, OPSX_TEXT_INPUT_ACTIONS, OpenSpecAdapter, OpenSpecDataHomeObserver, OpenSpecUIGlobalSettingsUpdateSchema, OpenSpecWatcher, OpsxConfigSchema, OpsxKernel, OwnedSpecIdentitySchema, PINNED_AGENT_GENERATOR_VERSION, PlanningCliProjectionDataSchema, PlanningCliProjectionSelectorSchema, PlanningCliProjectionStateSchema, ProjectBindingUpdateSchema, ProjectWatcher, PtyClientMessageSchema, RUNTIME_INVALIDATION_FACETS, ReactiveContext, ReactiveObservationEnvironment, ReactiveState, RuntimeInvalidationIndex, RuntimeRootInvalidationRegistry, ServiceTranslationEngineIdSchema, SpecIdentitySchema, StoreDoctorResultSchema, StoreListResultSchema, StoreMutationLifecycleEventSchema, StoreMutationStartResponseSchema, TRANSLATION_ENGINE_MANIFESTS, TerminalConfigSchema, TerminalControlParser, TerminalRendererEngineSchema, TranslationCacheReadInputSchema, TranslationCacheWriteInputSchema, TranslationEngineIdSchema, TranslationEngineLifecycleStatusSchema, acquireWatcher, applyAgentDeliveryPolicy, buildBackendHealthPayload, buildLocalDownloadPlanFromRepositoryFiles, buildRuntimePackageInstallCommand, checkLocalDirectionalModelLanguagePair, classifyStoreCliResult, clearCache, compareAndWriteAtomicPhysicalReactiveFile, computeEnvUri, constantTimeEqual, createCleanCliEnv, createPhysicalReactiveDirectory, createToolInitStateProjection, createTranslationEngineLifecycleStatus, detectRuntimePackageManager, getAllTools, getAvailableTools, getConfiguredTools, getDefaultCliCommandString, getDetectedProjectTools, getExternalCodexCommandObservationRoot, getManagedLocalTranslationEngineManifest, getOpsxEntityRootRelativePath, getRootContextCliSelector, getToolInitStates, getTranslationEngineLifecycleMessage, getWatcherRuntimeStatus, inferFileMime, inferFilePreviewKind, inspectActiveRootOfficialConfig, inspectEnvironmentDefaultStore, inspectProjectBinding, isDirectionalManagedLocalTranslationEngineId, isManagedLocalTranslationEngineId, isTerminalMutationStatus, isTranslationEngineDependencyReady, isTranslationEngineRuntimeReady, loadOpenSpecAgentCommandContents, normalizeAgentDeliveryPolicy, normalizeBatchTranslationError, parseCliCommandResult, parseOpsxEntityMetadata, parseOpsxSchemaDetail, patchActiveRootOfficialFields, reactiveReadFile, reactiveStat, removePhysicalReactivePath, replaceFileAtomically, requireCanonicalOpenSpecEntityId, requireCanonicalOpenSpecSpecId, requireOpenSpecEntityRelativePath, resolveCommandInvocation, resolveOpenSpecDataScope, resolveRootContext, resolveRuntimePackageInstallStrategy, resolveTerminalShellDefaults, runPhysicalReactivePathMutation, selectLocalDownloadGroup, shouldShowTranslationEngineInstallGate, sniffGlobalCli, spawnSafe, subscribeWatcherRuntimeStatus, terminalNotificationEventToPublishInput, terminateChildProcessTree, toCliProjectionCommandEvidence, toCliProjectionFailure, toCliProjectionNotice, toStoreFeatureResult, toggleMarkdownTask, updateProjectBindingContent, updateReactiveFileCache, validateActiveRootRawYaml, writePhysicalReactiveFile } from "@openspecui/core";
3
+ import { ActiveRootMutationSchema, ActiveRootRevisionSchema, AgentDeliveryPolicyUpdateSchema, BatchTranslateInputSchema, CliExecutor, CliProjectionCommandError, CliProjectionNoticeSchema, CodeEditorThemeSchema, ConfigManager, CustomSoundHashSchema, DASHBOARD_METRIC_KEYS, DashboardConfigSchema, DashboardGitSnapshotSchema, DashboardSummaryInvalidationSchema, DashboardSummaryProjectionStateSchema, DashboardTrendsProjectionSchema, DocumentTranslationConfigUpdateSchema, EnvironmentDefaultStoreUpdateSchema, EnvironmentGlobalConfigValueSchema, EnvironmentGlobalFileProjectionDataSchema, EnvironmentGlobalFileProjectionStateSchema, EnvironmentGlobalProjectionDataSchema, EnvironmentGlobalProjectionStateSchema, GitConfigSchema, GlobalSettingsManager, LocalModelAssetStateSchema, LocalModelLifecycleFileStateSchema, LocalModelLifecycleGroupStateSchema, LocalModelProfileManifestSchema, MarkdownParser, NotificationPublishInputSchema, NotificationSettingsSchema, OPENSPECUI_DOCUMENT_HOOK_VERSION, OPENSPECUI_WORKFLOW_HOOK_VERSION, OPSX_ARTIFACT_INPUT_ACTIONS, OPSX_CHANGE_INPUT_ACTIONS, OPSX_COMMAND_CAPABLE_WORKFLOWS, OPSX_CORE_PROFILE_WORKFLOWS, OPSX_TEXT_INPUT_ACTIONS, OpenSpecAdapter, OpenSpecDataHomeObserver, OpenSpecUIGlobalSettingsUpdateSchema, OpenSpecWatcher, OpsxConfigSchema, OpsxKernel, OwnedSpecIdentitySchema, PlanningCliProjectionDataSchema, PlanningCliProjectionSelectorSchema, PlanningCliProjectionStateSchema, ProjectBindingUpdateSchema, ProjectWatcher, PtyClientMessageSchema, RUNTIME_INVALIDATION_FACETS, ReactiveContext, ReactiveObservationEnvironment, ReactiveState, RuntimeInvalidationIndex, RuntimeRootInvalidationRegistry, ServiceTranslationEngineIdSchema, SpecIdentitySchema, StoreDoctorResultSchema, StoreListResultSchema, StoreMutationLifecycleEventSchema, StoreMutationStartResponseSchema, TRANSLATION_ENGINE_MANIFESTS, TerminalConfigSchema, TerminalControlParser, TerminalRendererEngineSchema, TranslationCacheReadInputSchema, TranslationCacheWriteInputSchema, TranslationEngineIdSchema, TranslationEngineLifecycleStatusSchema, acquireWatcher, applyAgentDeliveryPolicy, buildBackendHealthPayload, buildLocalDownloadPlanFromRepositoryFiles, buildRuntimePackageInstallCommand, checkLocalDirectionalModelLanguagePair, classifyStoreCliResult, clearCache, compareAndWriteAtomicPhysicalReactiveFile, computeEnvUri, constantTimeEqual, createCleanCliEnv, createPhysicalReactiveDirectory, createToolInitStateProjection, createTranslationEngineLifecycleStatus, detectRuntimePackageManager, getAllTools, getAvailableTools, getConfiguredTools, getDefaultCliCommandString, getDetectedProjectTools, getExternalAgentSkillsObservationRoots, getExternalCodexCommandObservationRoot, getManagedLocalTranslationEngineManifest, getOpsxEntityRootRelativePath, getRootContextCliSelector, getToolInitStates, getTranslationEngineLifecycleMessage, getWatcherRuntimeStatus, inferFileMime, inferFilePreviewKind, inspectActiveRootOfficialConfig, inspectEnvironmentDefaultStore, inspectProjectBinding, isDirectionalManagedLocalTranslationEngineId, isManagedLocalTranslationEngineId, isTerminalMutationStatus, isTranslationEngineDependencyReady, isTranslationEngineRuntimeReady, loadOpenSpecAgentCommandContents, normalizeAgentDeliveryPolicy, normalizeBatchTranslationError, parseCliCommandResult, parseOpsxEntityMetadata, parseOpsxSchemaDetail, patchActiveRootOfficialFields, reactiveReadFile, reactiveStat, removePhysicalReactivePath, replaceFileAtomically, requireCanonicalOpenSpecEntityId, requireCanonicalOpenSpecSpecId, requireOpenSpecEntityRelativePath, resolveCommandInvocation, resolveOpenSpecDataScope, resolveRootContext, resolveRuntimePackageInstallStrategy, resolveTerminalShellDefaults, runPhysicalReactivePathMutation, selectAgentDeliveryRegistry, selectLocalDownloadGroup, shouldShowTranslationEngineInstallGate, sniffGlobalCli, spawnSafe, subscribeWatcherRuntimeStatus, terminalNotificationEventToPublishInput, terminateChildProcessTree, toCliProjectionCommandEvidence, toCliProjectionFailure, toCliProjectionNotice, toStoreFeatureResult, toggleMarkdownTask, updateProjectBindingContent, updateReactiveFileCache, validateActiveRootRawYaml, writePhysicalReactiveFile } from "@openspecui/core";
4
4
  import { basename, dirname, extname, isAbsolute, join, matchesGlob, relative, resolve, sep } from "node:path";
5
5
  import { access, copyFile, link, lstat, mkdir, open, readFile, readlink, realpath, rename, rm, stat, symlink, unlink, writeFile } from "node:fs/promises";
6
6
  import { fileURLToPath, pathToFileURL } from "node:url";
@@ -33,6 +33,7 @@ import { NodeWorkerSearchProvider } from "@openspecui/search/node";
33
33
  import * as pty from "@lydell/node-pty";
34
34
  import { EventEmitter as EventEmitter$1 } from "events";
35
35
  import { HostedCliProjectionNoticeSchema, HostedRootContextProjectionStateSchema, HostedRootContextResolvedStateSchema, HostedStoreContentChangesProjectionStateSchema, HostedStoreContentSpecsProjectionStateSchema, HostedStoreDoctorEnvelopeSchema, HostedStoreDoctorProjectionStateSchema, HostedStoreListEnvelopeSchema, HostedStoreListProjectionStateSchema } from "@openspecui/core/hosted-contract";
36
+ import { deriveOpenSpecCliCapabilities, parseOpenSpecCliVersion } from "@openspecui/core/openspec-compat";
36
37
  import { StoreContentChangeListSchema, StoreContentSpecListSchema } from "@openspecui/core/store-content-projection";
37
38
  import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
38
39
  import { NodeSDK } from "@opentelemetry/sdk-node";
@@ -2983,13 +2984,15 @@ function resolveDefaultServerHostIdentity() {
2983
2984
  //#endregion
2984
2985
  //#region src/agent-delivery-projection-service.ts
2985
2986
  /**
2986
- * Orthogonal intents (created 2026-08-01 Asia/Shanghai):
2987
- * 1. Combine the complete OpenSpec 1.7 Agent registry with authoritative Environment delivery policy.
2987
+ * Orthogonal intents (updated 2026-08-15 Asia/Shanghai):
2988
+ * 1. Combine the complete OpenSpec 1.9 Agent registry with authoritative Environment delivery policy.
2988
2989
  * 2. Expose fresh one-shot physical projections through Core `getToolInitStates`.
2989
2990
  * 3. Retain reactive physical projections through Core `createToolInitStateProjection`.
2990
2991
  * 4. Rebind retained work when Environment policy changes and retire all work on dispose.
2992
+ * 5. Observe user-global skill roots (MiniMax Code) beside the project root and Codex prompt root.
2991
2993
  *
2992
2994
  * Original request (2026-08-01): "新增 Agent delivery projection service 及 checked tests。"
2995
+ * Original request (2026-08-15): "v9的适配需要同时适配 1.8和1.9。"
2993
2996
  */
2994
2997
  /** Raised when the CLI-owned Environment projection cannot provide an executable Agent policy. */
2995
2998
  var AgentDeliveryPolicyUnavailableError = class extends Error {
@@ -3034,8 +3037,8 @@ function cloneCleanup(cleanup) {
3034
3037
  replacementLabel: cleanup.replacementLabel
3035
3038
  };
3036
3039
  }
3037
- function cloneRegistry() {
3038
- return AI_TOOLS.map((tool) => ({
3040
+ function cloneRegistry(registry) {
3041
+ return registry.map((tool) => ({
3039
3042
  name: tool.name,
3040
3043
  value: tool.value,
3041
3044
  available: tool.available,
@@ -3046,6 +3049,9 @@ function cloneRegistry() {
3046
3049
  ...tool.detectionPaths ? { detectionPaths: [...tool.detectionPaths] } : {},
3047
3050
  ...tool.setupNote !== void 0 ? { setupNote: tool.setupNote } : {},
3048
3051
  ...tool.aliases ? { aliases: [...tool.aliases] } : {},
3052
+ ...tool.legacySkillsDirs ? { legacySkillsDirs: [...tool.legacySkillsDirs] } : {},
3053
+ ...tool.globalSkillsDir !== void 0 && tool.globalSkillsDir !== null ? { globalSkillsDir: tool.globalSkillsDir } : {},
3054
+ ...tool.requiresIdeRestart !== void 0 ? { requiresIdeRestart: tool.requiresIdeRestart } : {},
3049
3055
  ...tool.cleanup ? { cleanup: cloneCleanup(tool.cleanup) } : {},
3050
3056
  ...tool.migrations ? { migrations: tool.migrations.map((migration) => ({ ...migration })) } : {}
3051
3057
  }));
@@ -3064,16 +3070,16 @@ function policyFingerprint(policy) {
3064
3070
  policy.workflows
3065
3071
  ]);
3066
3072
  }
3067
- async function resolveGeneratorEvidence(cliExecutor, cliCommandAuthority, workflows) {
3073
+ async function resolveGeneratorEvidence(cliExecutor, cliCommandAuthority, workflows, delivery) {
3068
3074
  const availability = await cliExecutor.checkAvailability();
3069
3075
  return {
3070
- version: availability.available && availability.version ? availability.version : PINNED_AGENT_GENERATOR_VERSION,
3071
- commandContents: availability.available ? await loadOpenSpecAgentCommandContents(await cliCommandAuthority.getCliCommand(), workflows) : null
3076
+ version: availability.available && availability.version ? availability.version : null,
3077
+ commandContents: availability.available && delivery !== "skills" ? await loadOpenSpecAgentCommandContents(await cliCommandAuthority.getCliCommand(), workflows) : null
3072
3078
  };
3073
3079
  }
3074
- function createProjection(policy, states) {
3080
+ function createProjection(policy, states, generatorEvidence) {
3075
3081
  return {
3076
- registry: cloneRegistry(),
3082
+ registry: cloneRegistry(selectAgentDeliveryRegistry(generatorEvidence.version)),
3077
3083
  policy: {
3078
3084
  profile: policy.profile,
3079
3085
  delivery: policy.delivery,
@@ -3118,7 +3124,7 @@ var RetainedAgentDeliveryProjection = class {
3118
3124
  if (this.disposed || generation !== this.authorityGeneration) return;
3119
3125
  return this.preparePhysicalObservation().then(() => {
3120
3126
  if (this.disposed || generation !== this.authorityGeneration) return;
3121
- return resolveGeneratorEvidence(this.options.cliExecutor, this.options.cliCommandAuthority, environment.profileState.workflows).then((generatorEvidence) => {
3127
+ return resolveGeneratorEvidence(this.options.cliExecutor, this.options.cliCommandAuthority, environment.profileState.workflows, environment.profileState.delivery ?? "skills").then((generatorEvidence) => {
3122
3128
  if (this.disposed || generation !== this.authorityGeneration) return;
3123
3129
  this.startPhysicalProjection(resolvePolicy(environment), generatorEvidence, false);
3124
3130
  });
@@ -3134,7 +3140,7 @@ var RetainedAgentDeliveryProjection = class {
3134
3140
  }
3135
3141
  bindPolicy(policy, force) {
3136
3142
  const generation = ++this.authorityGeneration;
3137
- Promise.all([this.preparePhysicalObservation(), resolveGeneratorEvidence(this.options.cliExecutor, this.options.cliCommandAuthority, policy.workflows)]).then(([, generatorEvidence]) => {
3143
+ Promise.all([this.preparePhysicalObservation(), resolveGeneratorEvidence(this.options.cliExecutor, this.options.cliCommandAuthority, policy.workflows, policy.delivery)]).then(([, generatorEvidence]) => {
3138
3144
  if (this.disposed || generation !== this.authorityGeneration) return;
3139
3145
  this.startPhysicalProjection(policy, generatorEvidence, force);
3140
3146
  }).catch((cause) => {
@@ -3147,7 +3153,7 @@ var RetainedAgentDeliveryProjection = class {
3147
3153
  });
3148
3154
  }
3149
3155
  startPhysicalProjection(policy, generatorEvidence, force) {
3150
- const fingerprint = `${policyFingerprint(policy)}:${generatorEvidence.version}`;
3156
+ const fingerprint = `${policyFingerprint(policy)}:${generatorEvidence.version ?? "no-cli"}`;
3151
3157
  if (!force && this.physicalController && fingerprint === this.currentPolicyFingerprint) return;
3152
3158
  this.stopPhysicalProjection();
3153
3159
  this.currentPolicyFingerprint = fingerprint;
@@ -3157,8 +3163,10 @@ var RetainedAgentDeliveryProjection = class {
3157
3163
  const context = new ReactiveContext();
3158
3164
  const projectToolInitStates = createToolInitStateProjection(this.options.projectDir, {
3159
3165
  ...policy,
3160
- generatorVersion: generatorEvidence.version,
3161
- commandContents: generatorEvidence.commandContents
3166
+ generatorVersion: generatorEvidence.version ?? void 0,
3167
+ commandContents: generatorEvidence.commandContents?.catalog ?? null,
3168
+ unavailableCommandTools: generatorEvidence.commandContents?.unavailableTools ?? null,
3169
+ registry: selectAgentDeliveryRegistry(generatorEvidence.version)
3162
3170
  });
3163
3171
  (async () => {
3164
3172
  try {
@@ -3166,7 +3174,7 @@ var RetainedAgentDeliveryProjection = class {
3166
3174
  if (this.disposed || controller.signal.aborted || generation !== this.physicalGeneration) return;
3167
3175
  this.listener({
3168
3176
  type: "snapshot",
3169
- projection: createProjection(policy, states)
3177
+ projection: createProjection(policy, states, generatorEvidence)
3170
3178
  });
3171
3179
  }
3172
3180
  } catch (cause) {
@@ -3200,14 +3208,16 @@ var AgentDeliveryProjectionService = class {
3200
3208
  const environment = await this.options.environmentGlobalProjectionService.getCurrent();
3201
3209
  this.assertActive();
3202
3210
  const policy = resolvePolicy(environment);
3203
- const generatorEvidence = await resolveGeneratorEvidence(this.options.cliExecutor, this.options.cliCommandAuthority, policy.workflows);
3211
+ const generatorEvidence = await resolveGeneratorEvidence(this.options.cliExecutor, this.options.cliCommandAuthority, policy.workflows, policy.delivery);
3204
3212
  const states = await getToolInitStates(this.options.projectDir, {
3205
3213
  ...policy,
3206
- generatorVersion: generatorEvidence.version,
3207
- commandContents: generatorEvidence.commandContents
3214
+ generatorVersion: generatorEvidence.version ?? void 0,
3215
+ commandContents: generatorEvidence.commandContents?.catalog ?? null,
3216
+ unavailableCommandTools: generatorEvidence.commandContents?.unavailableTools ?? null,
3217
+ registry: selectAgentDeliveryRegistry(generatorEvidence.version)
3208
3218
  });
3209
3219
  this.assertActive();
3210
- return createProjection(policy, states);
3220
+ return createProjection(policy, states, generatorEvidence);
3211
3221
  }
3212
3222
  /** Retain Environment policy and physical Agent artifact observation until unsubscribed. */
3213
3223
  subscribe(listener) {
@@ -3249,6 +3259,7 @@ var AgentDeliveryProjectionService = class {
3249
3259
  try {
3250
3260
  releases.push(await this.options.observationEnvironment.acquireRoot(this.options.projectDir));
3251
3261
  releases.push(await this.options.observationEnvironment.acquireRoot(getExternalCodexCommandObservationRoot()));
3262
+ for (const globalSkillsRoot of getExternalAgentSkillsObservationRoots()) releases.push(await this.options.observationEnvironment.acquireRoot(globalSkillsRoot));
3252
3263
  if (this.disposed) {
3253
3264
  await Promise.all(releases.map((release) => release()));
3254
3265
  return;
@@ -4116,13 +4127,26 @@ var ChangesProjectionService = class {
4116
4127
  load: async (context) => {
4117
4128
  context.reportStage("root-ready");
4118
4129
  const ids = await this.options.adapter.listChanges();
4130
+ const cliEntries = await this.options.adapter.readCliChangeListEntries?.() ?? /* @__PURE__ */ new Map();
4119
4131
  const rows = [];
4120
4132
  const errors = [];
4121
4133
  const total = ids.length;
4122
4134
  for (const [index, id] of ids.entries()) {
4123
4135
  if (context.signal.aborted) throw new DOMException("Change projection was cancelled.", "AbortError");
4124
4136
  try {
4125
- const row = await this.options.adapter.readChangeMeta(id);
4137
+ const meta = await this.options.adapter.readChangeMeta(id);
4138
+ const entry = cliEntries.get(id);
4139
+ const row = entry ? {
4140
+ ...meta,
4141
+ cliTaskSummary: {
4142
+ completedTasks: entry.completedTasks,
4143
+ totalTasks: entry.totalTasks,
4144
+ status: entry.status
4145
+ }
4146
+ } : {
4147
+ ...meta,
4148
+ cliTaskSummary: meta.cliTaskSummary ?? null
4149
+ };
4126
4150
  rows.push(row);
4127
4151
  context.emitBatch({
4128
4152
  rows: [row],
@@ -9340,17 +9364,26 @@ function selectRecentDashboardArchives(items, limit = DASHBOARD_RECENT_LIST_LIMI
9340
9364
  //#region src/dashboard-summary.ts
9341
9365
  /** Read the objective files behind Dashboard Summary without invoking Git or workflow commands. */
9342
9366
  async function loadDashboardPlanningFacts(ctx) {
9343
- const [specMetas, changeMetas, archiveMetas] = await Promise.all([
9367
+ const [specMetas, changeMetas, archiveMetas, cliEntries] = await Promise.all([
9344
9368
  ctx.adapter.listSpecsWithMeta(),
9345
9369
  ctx.adapter.listChangesWithMeta(),
9346
- ctx.adapter.listArchivedChangesWithMeta()
9370
+ ctx.adapter.listArchivedChangesWithMeta(),
9371
+ ctx.readCliChangeListEntries?.().catch(() => /* @__PURE__ */ new Map()) ?? Promise.resolve(/* @__PURE__ */ new Map())
9347
9372
  ]);
9348
- const allActiveChanges = changeMetas.map((changeMeta) => ({
9349
- id: changeMeta.id,
9350
- name: changeMeta.name ?? changeMeta.id,
9351
- trackedTaskProgress: changeMeta.trackedTaskProgress,
9352
- updatedAt: changeMeta.updatedAt
9353
- }));
9373
+ const allActiveChanges = changeMetas.map((changeMeta) => {
9374
+ const entry = cliEntries.get(changeMeta.id);
9375
+ return {
9376
+ id: changeMeta.id,
9377
+ name: changeMeta.name ?? changeMeta.id,
9378
+ trackedTaskProgress: changeMeta.trackedTaskProgress,
9379
+ cliTaskSummary: entry ? {
9380
+ completedTasks: entry.completedTasks,
9381
+ totalTasks: entry.totalTasks,
9382
+ status: entry.status
9383
+ } : changeMeta.cliTaskSummary,
9384
+ updatedAt: changeMeta.updatedAt
9385
+ };
9386
+ });
9354
9387
  return {
9355
9388
  specMetas,
9356
9389
  archiveMetas,
@@ -10881,6 +10914,18 @@ var PlanningRootUnavailableError = class extends Error {
10881
10914
  }
10882
10915
  };
10883
10916
  /** Serialized deep owner for one replaceable Planning-root service record. */
10917
+ /** Read CLI Change-list task facts once per call; failures degrade to an empty index. */
10918
+ function readCliChangeListEntriesFor(service) {
10919
+ return async () => {
10920
+ try {
10921
+ const data = await service.getCurrent({ kind: "opsx-change-list" });
10922
+ if (data.kind !== "opsx-change-list") return /* @__PURE__ */ new Map();
10923
+ return new Map(data.entries.map((entry) => [entry.name, entry]));
10924
+ } catch {
10925
+ return /* @__PURE__ */ new Map();
10926
+ }
10927
+ };
10928
+ }
10884
10929
  var PlanningRootServiceManager = class {
10885
10930
  activeRecord = null;
10886
10931
  currentRootContextSnapshot = null;
@@ -10956,7 +11001,10 @@ var PlanningRootServiceManager = class {
10956
11001
  },
10957
11002
  codeGitBindingToken: this.options.codeBinding.bindingToken,
10958
11003
  loaders: {
10959
- loadSummary: () => loadDashboardSummary({ adapter }),
11004
+ loadSummary: () => loadDashboardSummary({
11005
+ adapter,
11006
+ readCliChangeListEntries: readCliChangeListEntriesFor(planningCliProjectionService)
11007
+ }),
10960
11008
  loadTrends: () => loadDashboardTrends({
10961
11009
  adapter,
10962
11010
  configManager: this.options.configManager
@@ -10967,16 +11015,6 @@ var PlanningRootServiceManager = class {
10967
11015
  }, "projection-work", signal)
10968
11016
  }
10969
11017
  });
10970
- const changesProjectionService = new ChangesProjectionService({
10971
- workOwner: this.changesProjectionWorkOwner,
10972
- root: {
10973
- path: projectDir,
10974
- source: planningRoot.source,
10975
- storeSelector: rootCliSelector.store ?? null,
10976
- generation: gitBindingToken
10977
- },
10978
- adapter
10979
- });
10980
11018
  const planningCliProjectionService = new PlanningCliProjectionService({
10981
11019
  rootContext: rootContextValue,
10982
11020
  gitBindingToken,
@@ -10987,6 +11025,28 @@ var PlanningRootServiceManager = class {
10987
11025
  storeObservation: this.options.storeObservation,
10988
11026
  workOwner: this.planningCliProjectionWorkOwner
10989
11027
  });
11028
+ const changesProjectionService = new ChangesProjectionService({
11029
+ workOwner: this.changesProjectionWorkOwner,
11030
+ root: {
11031
+ path: projectDir,
11032
+ source: planningRoot.source,
11033
+ storeSelector: rootCliSelector.store ?? null,
11034
+ generation: gitBindingToken
11035
+ },
11036
+ adapter: {
11037
+ listChanges: () => adapter.listChanges(),
11038
+ readChangeMeta: (id) => adapter.readChangeMeta(id),
11039
+ readCliChangeListEntries: async () => {
11040
+ try {
11041
+ const data = await planningCliProjectionService.getCurrent({ kind: "opsx-change-list" });
11042
+ if (data.kind !== "opsx-change-list") return /* @__PURE__ */ new Map();
11043
+ return new Map(data.entries.map((entry) => [entry.name, entry]));
11044
+ } catch {
11045
+ return /* @__PURE__ */ new Map();
11046
+ }
11047
+ }
11048
+ }
11049
+ });
10990
11050
  const filePreviewService = new FilePreviewService(projectDir, this.options.previewAssetsDir);
10991
11051
  const schemaMutationService = new SchemaMutationService({
10992
11052
  planningRoot: projectDir,
@@ -14703,7 +14763,16 @@ const changeProjectionDataSchema = z.object({
14703
14763
  remaining: z.number().int().nonnegative()
14704
14764
  }),
14705
14765
  createdAt: z.number(),
14706
- updatedAt: z.number()
14766
+ updatedAt: z.number(),
14767
+ cliTaskSummary: z.object({
14768
+ completedTasks: z.number().int().nonnegative(),
14769
+ totalTasks: z.number().int().nonnegative(),
14770
+ status: z.enum([
14771
+ "no-tasks",
14772
+ "complete",
14773
+ "in-progress"
14774
+ ])
14775
+ }).nullable()
14707
14776
  })),
14708
14777
  errors: z.array(changeProjectionRowErrorSchema)
14709
14778
  });
@@ -15345,12 +15414,31 @@ const specRouter = router({
15345
15414
  /**
15346
15415
  * Change router - change proposal operations
15347
15416
  */
15417
+ /**
15418
+ * Join the CLI-owned Change-list task facts onto adapter ChangeMeta rows. The CLI entry is
15419
+ * the task-count authority (`openspec list` reports the same checkbox semantics as Apply);
15420
+ * rows the CLI did not list keep a null summary rather than a UI-side recomputation.
15421
+ */
15422
+ async function listChangesWithCliTaskSummary(ctx, adapter) {
15423
+ const [rows, cliList] = await Promise.all([adapter.listChangesWithMeta(), runPlanningRootRead(ctx, ({ planningCliProjectionService }) => planningCliProjectionService.getCurrent({ kind: "opsx-change-list" })).then((data) => data.kind === "opsx-change-list" ? new Map(data.entries.map((entry) => [entry.name, entry])) : /* @__PURE__ */ new Map(), () => /* @__PURE__ */ new Map())]);
15424
+ return rows.map((row) => {
15425
+ const entry = cliList.get(row.id);
15426
+ return entry ? {
15427
+ ...row,
15428
+ cliTaskSummary: {
15429
+ completedTasks: entry.completedTasks,
15430
+ totalTasks: entry.totalTasks,
15431
+ status: entry.status
15432
+ }
15433
+ } : row;
15434
+ });
15435
+ }
15348
15436
  const changeRouter = router({
15349
15437
  list: publicProcedure.query(async ({ ctx }) => {
15350
15438
  return runPlanningRootRead(ctx, ({ adapter }) => adapter.listChanges());
15351
15439
  }),
15352
15440
  listWithMeta: publicProcedure.query(async ({ ctx }) => {
15353
- return runPlanningRootRead(ctx, ({ adapter }) => adapter.listChangesWithMeta());
15441
+ return runPlanningRootRead(ctx, ({ adapter }) => listChangesWithCliTaskSummary(ctx, adapter));
15354
15442
  }),
15355
15443
  listArchived: publicProcedure.query(async ({ ctx }) => {
15356
15444
  return runPlanningRootRead(ctx, ({ adapter }) => adapter.listArchivedChanges());
@@ -15395,7 +15483,7 @@ const changeRouter = router({
15395
15483
  return { success: true };
15396
15484
  }),
15397
15485
  subscribe: publicProcedure.subscription(({ ctx }) => {
15398
- return createPlanningRootSubscription(ctx, ({ adapter }) => adapter.listChangesWithMeta());
15486
+ return createPlanningRootSubscription(ctx, ({ adapter }) => listChangesWithCliTaskSummary(ctx, adapter));
15399
15487
  }),
15400
15488
  subscribeBatches: publicProcedure.subscription(({ ctx }) => createPlanningRootProjectionWorkSubscription(ctx, (services, listener) => services.changesProjectionService.subscribe(listener), (event) => changeProjectionEventSchema().parse(event))),
15401
15489
  subscribeFiles: publicProcedure.input(z.object({ id: z.string() })).subscription(({ ctx, input }) => {
@@ -15673,32 +15761,45 @@ const cliRouter = router({
15673
15761
  subscribeConfiguredTools: publicProcedure.subscription(({ ctx }) => {
15674
15762
  return createReactiveSubscription(() => getConfiguredTools(ctx.projectDir));
15675
15763
  }),
15676
- validate: publicProcedure.input(z.discriminatedUnion("kind", [z.object({
15677
- kind: z.literal("item"),
15678
- id: z.string().min(1),
15679
- type: z.enum(["spec", "change"]).optional(),
15680
- strict: z.boolean().optional()
15681
- }), z.object({
15682
- kind: z.literal("scope"),
15683
- scope: z.enum([
15684
- "all",
15685
- "changes",
15686
- "specs"
15687
- ]),
15688
- strict: z.boolean().optional()
15689
- })])).mutation(async ({ ctx, input }) => {
15690
- return runPlanningRoot(ctx, ({ rootContext }) => ctx.cliExecutor.contracts.validate({
15691
- target: input.kind === "item" ? {
15692
- kind: "item",
15693
- id: input.id,
15694
- type: input.type
15695
- } : {
15696
- kind: "scope",
15697
- scope: input.scope
15698
- },
15699
- strict: input.strict,
15700
- ...getRootContextCliSelector(rootContext)
15701
- }));
15764
+ validate: publicProcedure.input(z.discriminatedUnion("kind", [
15765
+ z.object({
15766
+ kind: z.literal("item"),
15767
+ id: z.string().min(1),
15768
+ type: z.enum(["spec", "change"]).optional(),
15769
+ strict: z.boolean().optional()
15770
+ }),
15771
+ z.object({
15772
+ kind: z.literal("scope"),
15773
+ scope: z.enum([
15774
+ "all",
15775
+ "changes",
15776
+ "specs"
15777
+ ]),
15778
+ strict: z.boolean().optional()
15779
+ }),
15780
+ z.object({ kind: z.literal("archived") })
15781
+ ])).mutation(async ({ ctx, input }) => {
15782
+ return runPlanningRoot(ctx, ({ rootContext }) => {
15783
+ if (input.kind === "archived") {
15784
+ const cli = rootContext.cli;
15785
+ if (!deriveOpenSpecCliCapabilities(cli.available ? parseOpenSpecCliVersion(cli.version) : null).archivedValidation) throw new TRPCError({
15786
+ code: "PRECONDITION_FAILED",
15787
+ message: "Archived task validation requires the detected OpenSpec CLI to declare the 1.9 archived-validation capability."
15788
+ });
15789
+ }
15790
+ return ctx.cliExecutor.contracts.validate({
15791
+ target: input.kind === "item" ? {
15792
+ kind: "item",
15793
+ id: input.id,
15794
+ type: input.type
15795
+ } : input.kind === "scope" ? {
15796
+ kind: "scope",
15797
+ scope: input.scope
15798
+ } : { kind: "archived" },
15799
+ strict: input.kind === "archived" ? void 0 : input.strict,
15800
+ ...getRootContextCliSelector(rootContext)
15801
+ });
15802
+ });
15702
15803
  }),
15703
15804
  validateStream: publicProcedure.input(z.object({
15704
15805
  type: z.enum(["spec", "change"]).optional(),
@@ -15752,7 +15853,7 @@ const cliRouter = router({
15752
15853
  })
15753
15854
  });
15754
15855
  /** Config-owned Agent registry, policy, physical inventory, and CLI mutation boundary. */
15755
- const agentIntegrationToolIdSchema = z.string().min(1).refine((toolId) => getAvailableTools().some((tool) => tool.value === toolId), "Agent tool must be an available OpenSpec 1.7 registry id.");
15856
+ const agentIntegrationToolIdSchema = z.string().min(1).refine((toolId) => getAvailableTools().some((tool) => tool.value === toolId), "Agent tool must be an available OpenSpec 1.9 registry id.");
15756
15857
  const agentIntegrationsRouter = router({
15757
15858
  get: publicProcedure.query(({ ctx }) => ctx.agentDeliveryProjectionService.getCurrent()),
15758
15859
  refresh: publicProcedure.query(({ ctx }) => ctx.agentDeliveryProjectionService.refresh()),
@@ -15780,6 +15881,14 @@ const agentIntegrationsRouter = router({
15780
15881
  })).subscription(({ ctx, input }) => {
15781
15882
  return createCliStreamObservable(async (onEvent) => {
15782
15883
  const projection = await ctx.agentDeliveryProjectionService.getCurrent();
15884
+ if (Array.isArray(input.tools)) {
15885
+ const offered = new Set(projection.registry.map((tool) => tool.value));
15886
+ const unavailable = input.tools.filter((toolId) => !offered.has(toolId));
15887
+ if (unavailable.length > 0) throw new TRPCError({
15888
+ code: "PRECONDITION_FAILED",
15889
+ message: `Agent tool(s) unavailable on the admitted OpenSpec CLI line: ${unavailable.join(", ")}.`
15890
+ });
15891
+ }
15783
15892
  return streamOpenSpecCliMutation(ctx, ["init"], (mutationEvent) => ctx.cliExecutor.initStream({
15784
15893
  tools: input.tools,
15785
15894
  profile: projection.policy.profile,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openspecui/server",
3
- "version": "7.0.2",
3
+ "version": "9.0.1",
4
4
  "type": "module",
5
5
  "main": "dist/index.mjs",
6
6
  "exports": {
@@ -33,8 +33,8 @@
33
33
  "@hono/node-server": "^1.14.1",
34
34
  "@huggingface/hub": "^2.12.0",
35
35
  "@lydell/node-pty": "^1.1.0",
36
- "@openspecui/core": "7.0.2",
37
- "@openspecui/search": "7.0.2",
36
+ "@openspecui/core": "9.0.1",
37
+ "@openspecui/search": "9.0.1",
38
38
  "@opentelemetry/api": "^1.9.1",
39
39
  "@opentelemetry/exporter-trace-otlp-http": "^0.221.0",
40
40
  "@opentelemetry/sdk-node": "^0.221.0",