@openspecui/server 6.2.0 → 7.0.0

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 +842 -222
  2. package/package.json +10 -5
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 { BatchTranslateInputSchema, CliExecutor, CliProjectionCommandError, CliProjectionNoticeSchema, CodeEditorThemeSchema, ConfigManager, CustomSoundHashSchema, DASHBOARD_METRIC_KEYS, DashboardConfigSchema, DashboardGitSnapshotSchema, DashboardSummaryInvalidationSchema, DashboardSummaryProjectionStateSchema, DashboardTrendsProjectionSchema, DocumentTranslationConfigUpdateSchema, 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, buildBackendHealthPayload, buildLocalDownloadPlanFromRepositoryFiles, buildRuntimePackageInstallCommand, checkLocalDirectionalModelLanguagePair, classifyStoreCliResult, clearCache, computeEnvUri, constantTimeEqual, createCleanCliEnv, createPhysicalReactiveDirectory, createToolInitStateProjection, createTranslationEngineLifecycleStatus, detectRuntimePackageManager, getAllTools, getAvailableTools, getConfiguredTools, getDefaultCliCommandString, getDetectedProjectTools, getExternalCodexCommandObservationRoot, getManagedLocalTranslationEngineManifest, getOpsxEntityRootRelativePath, getRootContextCliSelector, getTranslationEngineLifecycleMessage, getWatcherRuntimeStatus, inferFileMime, inferFilePreviewKind, inspectProjectBinding, isDirectionalManagedLocalTranslationEngineId, isManagedLocalTranslationEngineId, isTerminalMutationStatus, isTranslationEngineDependencyReady, isTranslationEngineRuntimeReady, normalizeBatchTranslationError, parseCliCommandResult, parseOpsxEntityMetadata, parseOpsxSchemaDetail, reactiveReadFile, reactiveStat, removePhysicalReactivePath, requireCanonicalOpenSpecEntityId, requireOpenSpecEntityRelativePath, resolveOpenSpecDataScope, resolveRootContext, resolveRuntimePackageInstallStrategy, resolveTerminalShellDefaults, runPhysicalReactivePathMutation, selectLocalDownloadGroup, shouldShowTranslationEngineInstallGate, sniffGlobalCli, subscribeWatcherRuntimeStatus, terminalNotificationEventToPublishInput, toCliProjectionCommandEvidence, toCliProjectionFailure, toCliProjectionNotice, toStoreFeatureResult, toggleMarkdownTask, updateProjectBindingContent, updateReactiveFileCache, writePhysicalReactiveFile } from "@openspecui/core";
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, requireCanonicalOpenSpecEntityId, requireCanonicalOpenSpecSpecId, requireOpenSpecEntityRelativePath, resolveOpenSpecDataScope, resolveRootContext, resolveRuntimePackageInstallStrategy, resolveTerminalShellDefaults, runPhysicalReactivePathMutation, selectLocalDownloadGroup, shouldShowTranslationEngineInstallGate, sniffGlobalCli, subscribeWatcherRuntimeStatus, terminalNotificationEventToPublishInput, 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, lstat, mkdir, open, readFile, readlink, realpath, rename, rm, stat, symlink, unlink, writeFile } from "node:fs/promises";
6
6
  import { fileURLToPath, pathToFileURL } from "node:url";
@@ -25,7 +25,7 @@ import { execFile, spawn } from "node:child_process";
25
25
  import { promisify } from "node:util";
26
26
  import { NotificationGroupKeySchema, NotificationPublishInputSchema as NotificationPublishInputSchema$1, getNotificationGroupKey } from "@openspecui/core/notifications";
27
27
  import { match } from "ts-pattern";
28
- import { buildSpecCatalog, specIdentityKey, specRoutePath } from "@openspecui/core/spec-catalog";
28
+ import { buildSpecCatalog, specDocumentDisplayPath, specIdentityKey, specRoutePath } from "@openspecui/core/spec-catalog";
29
29
  import { DiagConsoleLogger, DiagLogLevel, SpanStatusCode, diag, trace } from "@opentelemetry/api";
30
30
  import { EventEmitter } from "node:events";
31
31
  import { ProjectSearchDocumentSchema, ProjectSearchHitSchema, ProjectSearchQuerySchema, parseProjectSearchHits } from "@openspecui/search";
@@ -2945,6 +2945,290 @@ function resolveDefaultServerHostIdentity() {
2945
2945
  return `hostname:${value}`;
2946
2946
  }
2947
2947
 
2948
+ //#endregion
2949
+ //#region src/agent-delivery-projection-service.ts
2950
+ /**
2951
+ * Orthogonal intents (created 2026-08-01 Asia/Shanghai):
2952
+ * 1. Combine the complete OpenSpec 1.7 Agent registry with authoritative Environment delivery policy.
2953
+ * 2. Expose fresh one-shot physical projections through Core `getToolInitStates`.
2954
+ * 3. Retain reactive physical projections through Core `createToolInitStateProjection`.
2955
+ * 4. Rebind retained work when Environment policy changes and retire all work on dispose.
2956
+ *
2957
+ * Original request (2026-08-01): "新增 Agent delivery projection service 及 checked tests。"
2958
+ */
2959
+ /** Raised when the CLI-owned Environment projection cannot provide an executable Agent policy. */
2960
+ var AgentDeliveryPolicyUnavailableError = class extends Error {
2961
+ constructor(message) {
2962
+ super(message);
2963
+ this.name = "AgentDeliveryPolicyUnavailableError";
2964
+ }
2965
+ };
2966
+ function cloneCommandContent(content) {
2967
+ if (content.kind === "toml") return {
2968
+ kind: content.kind,
2969
+ fields: [...content.fields]
2970
+ };
2971
+ return {
2972
+ kind: content.kind,
2973
+ frontmatter: content.frontmatter.kind === "yaml" ? {
2974
+ kind: content.frontmatter.kind,
2975
+ fields: [...content.frontmatter.fields]
2976
+ } : { kind: content.frontmatter.kind },
2977
+ bodyLayout: content.bodyLayout
2978
+ };
2979
+ }
2980
+ function cloneCommand(command) {
2981
+ if (!command) return null;
2982
+ return {
2983
+ ...command,
2984
+ content: cloneCommandContent(command.content),
2985
+ invocation: { ...command.invocation },
2986
+ ...command.legacyPathTemplates ? { legacyPathTemplates: [...command.legacyPathTemplates] } : {}
2987
+ };
2988
+ }
2989
+ function cloneCleanup(cleanup) {
2990
+ if (!cleanup) return void 0;
2991
+ if (cleanup.kind === "project-patterns") return {
2992
+ kind: cleanup.kind,
2993
+ patterns: [...cleanup.patterns]
2994
+ };
2995
+ return {
2996
+ kind: cleanup.kind,
2997
+ projectPatterns: [...cleanup.projectPatterns],
2998
+ managedFiles: Object.fromEntries(Object.entries(cleanup.managedFiles).map(([fileName, workflows]) => [fileName, [...workflows]])),
2999
+ replacementLabel: cleanup.replacementLabel
3000
+ };
3001
+ }
3002
+ function cloneRegistry() {
3003
+ return AI_TOOLS.map((tool) => ({
3004
+ name: tool.name,
3005
+ value: tool.value,
3006
+ available: tool.available,
3007
+ skillsDir: tool.skillsDir,
3008
+ capability: tool.capability,
3009
+ command: cloneCommand(tool.command),
3010
+ ...tool.successLabel !== void 0 ? { successLabel: tool.successLabel } : {},
3011
+ ...tool.detectionPaths ? { detectionPaths: [...tool.detectionPaths] } : {},
3012
+ ...tool.setupNote !== void 0 ? { setupNote: tool.setupNote } : {},
3013
+ ...tool.aliases ? { aliases: [...tool.aliases] } : {},
3014
+ ...tool.cleanup ? { cleanup: cloneCleanup(tool.cleanup) } : {},
3015
+ ...tool.migrations ? { migrations: tool.migrations.map((migration) => ({ ...migration })) } : {}
3016
+ }));
3017
+ }
3018
+ function resolvePolicy(environment) {
3019
+ const { profileState } = environment;
3020
+ if (!profileState.available) throw new AgentDeliveryPolicyUnavailableError(profileState.error ?? "Environment Agent delivery policy is unavailable.");
3021
+ if (!profileState.profile) throw new AgentDeliveryPolicyUnavailableError("Environment Agent delivery profile is unavailable.");
3022
+ if (!profileState.delivery) throw new AgentDeliveryPolicyUnavailableError("Environment Agent delivery mode is unavailable.");
3023
+ return { ...normalizeAgentDeliveryPolicy(profileState) };
3024
+ }
3025
+ function policyFingerprint(policy) {
3026
+ return JSON.stringify([
3027
+ policy.profile,
3028
+ policy.delivery,
3029
+ policy.workflows
3030
+ ]);
3031
+ }
3032
+ async function resolveGeneratorEvidence(cliExecutor, cliCommandAuthority, workflows) {
3033
+ const availability = await cliExecutor.checkAvailability();
3034
+ return {
3035
+ version: availability.available && availability.version ? availability.version : PINNED_AGENT_GENERATOR_VERSION,
3036
+ commandContents: availability.available ? await loadOpenSpecAgentCommandContents(await cliCommandAuthority.getCliCommand(), workflows) : null
3037
+ };
3038
+ }
3039
+ function createProjection(policy, states) {
3040
+ return {
3041
+ registry: cloneRegistry(),
3042
+ policy: {
3043
+ profile: policy.profile,
3044
+ delivery: policy.delivery,
3045
+ workflows: [...policy.workflows]
3046
+ },
3047
+ states: [...states]
3048
+ };
3049
+ }
3050
+ var RetainedAgentDeliveryProjection = class {
3051
+ authorityGeneration = 0;
3052
+ environmentSubscription = null;
3053
+ physicalController = null;
3054
+ physicalGeneration = 0;
3055
+ currentPolicyFingerprint = null;
3056
+ disposed = false;
3057
+ constructor(options, listener, preparePhysicalObservation, onDispose) {
3058
+ this.options = options;
3059
+ this.listener = listener;
3060
+ this.preparePhysicalObservation = preparePhysicalObservation;
3061
+ this.onDispose = onDispose;
3062
+ this.environmentSubscription = options.environmentGlobalProjectionService.subscribe(() => {
3063
+ this.rebindFromEnvironment();
3064
+ });
3065
+ this.rebindFromEnvironment();
3066
+ }
3067
+ replacePolicy(policy) {
3068
+ if (this.disposed) return;
3069
+ this.bindPolicy(policy, true);
3070
+ }
3071
+ unsubscribe() {
3072
+ if (this.disposed) return;
3073
+ this.disposed = true;
3074
+ this.authorityGeneration += 1;
3075
+ this.environmentSubscription?.unsubscribe();
3076
+ this.environmentSubscription = null;
3077
+ this.stopPhysicalProjection();
3078
+ this.onDispose();
3079
+ }
3080
+ rebindFromEnvironment() {
3081
+ const generation = ++this.authorityGeneration;
3082
+ this.options.environmentGlobalProjectionService.getCurrent().then((environment) => {
3083
+ if (this.disposed || generation !== this.authorityGeneration) return;
3084
+ return this.preparePhysicalObservation().then(() => {
3085
+ if (this.disposed || generation !== this.authorityGeneration) return;
3086
+ return resolveGeneratorEvidence(this.options.cliExecutor, this.options.cliCommandAuthority, environment.profileState.workflows).then((generatorEvidence) => {
3087
+ if (this.disposed || generation !== this.authorityGeneration) return;
3088
+ this.startPhysicalProjection(resolvePolicy(environment), generatorEvidence, false);
3089
+ });
3090
+ });
3091
+ }).catch((cause) => {
3092
+ if (this.disposed || generation !== this.authorityGeneration) return;
3093
+ this.stopPhysicalProjection();
3094
+ this.listener({
3095
+ type: "failed",
3096
+ error: cause instanceof Error ? cause : new Error(String(cause))
3097
+ });
3098
+ });
3099
+ }
3100
+ bindPolicy(policy, force) {
3101
+ const generation = ++this.authorityGeneration;
3102
+ Promise.all([this.preparePhysicalObservation(), resolveGeneratorEvidence(this.options.cliExecutor, this.options.cliCommandAuthority, policy.workflows)]).then(([, generatorEvidence]) => {
3103
+ if (this.disposed || generation !== this.authorityGeneration) return;
3104
+ this.startPhysicalProjection(policy, generatorEvidence, force);
3105
+ }).catch((cause) => {
3106
+ if (this.disposed || generation !== this.authorityGeneration) return;
3107
+ this.stopPhysicalProjection();
3108
+ this.listener({
3109
+ type: "failed",
3110
+ error: cause instanceof Error ? cause : new Error(String(cause))
3111
+ });
3112
+ });
3113
+ }
3114
+ startPhysicalProjection(policy, generatorEvidence, force) {
3115
+ const fingerprint = `${policyFingerprint(policy)}:${generatorEvidence.version}`;
3116
+ if (!force && this.physicalController && fingerprint === this.currentPolicyFingerprint) return;
3117
+ this.stopPhysicalProjection();
3118
+ this.currentPolicyFingerprint = fingerprint;
3119
+ const generation = ++this.physicalGeneration;
3120
+ const controller = new AbortController();
3121
+ this.physicalController = controller;
3122
+ const context = new ReactiveContext();
3123
+ const projectToolInitStates = createToolInitStateProjection(this.options.projectDir, {
3124
+ ...policy,
3125
+ generatorVersion: generatorEvidence.version,
3126
+ commandContents: generatorEvidence.commandContents
3127
+ });
3128
+ (async () => {
3129
+ try {
3130
+ for await (const states of context.stream(projectToolInitStates, controller.signal)) {
3131
+ if (this.disposed || controller.signal.aborted || generation !== this.physicalGeneration) return;
3132
+ this.listener({
3133
+ type: "snapshot",
3134
+ projection: createProjection(policy, states)
3135
+ });
3136
+ }
3137
+ } catch (cause) {
3138
+ if (this.disposed || controller.signal.aborted || generation !== this.physicalGeneration) return;
3139
+ this.listener({
3140
+ type: "failed",
3141
+ error: cause instanceof Error ? cause : new Error(String(cause))
3142
+ });
3143
+ }
3144
+ })();
3145
+ }
3146
+ stopPhysicalProjection() {
3147
+ this.physicalGeneration += 1;
3148
+ this.physicalController?.abort();
3149
+ this.physicalController = null;
3150
+ this.currentPolicyFingerprint = null;
3151
+ }
3152
+ };
3153
+ /** Server-owned Agent registry and physical delivery projection boundary. */
3154
+ var AgentDeliveryProjectionService = class {
3155
+ subscriptions = /* @__PURE__ */ new Set();
3156
+ observationStartPromise = null;
3157
+ observationReleases = [];
3158
+ disposed = false;
3159
+ constructor(options) {
3160
+ this.options = options;
3161
+ }
3162
+ /** Read one fresh projection from the current Environment policy and physical files. */
3163
+ async getCurrent() {
3164
+ this.assertActive();
3165
+ const environment = await this.options.environmentGlobalProjectionService.getCurrent();
3166
+ this.assertActive();
3167
+ const policy = resolvePolicy(environment);
3168
+ const generatorEvidence = await resolveGeneratorEvidence(this.options.cliExecutor, this.options.cliCommandAuthority, policy.workflows);
3169
+ const states = await getToolInitStates(this.options.projectDir, {
3170
+ ...policy,
3171
+ generatorVersion: generatorEvidence.version,
3172
+ commandContents: generatorEvidence.commandContents
3173
+ });
3174
+ this.assertActive();
3175
+ return createProjection(policy, states);
3176
+ }
3177
+ /** Retain Environment policy and physical Agent artifact observation until unsubscribed. */
3178
+ subscribe(listener) {
3179
+ this.assertActive();
3180
+ let subscription;
3181
+ subscription = new RetainedAgentDeliveryProjection(this.options, listener, () => this.ensurePhysicalObservation(), () => {
3182
+ this.subscriptions.delete(subscription);
3183
+ });
3184
+ this.subscriptions.add(subscription);
3185
+ return subscription;
3186
+ }
3187
+ /** Refresh Environment authority, return a fresh Pull, and replace every retained projection. */
3188
+ async refresh() {
3189
+ this.assertActive();
3190
+ this.options.environmentGlobalProjectionService.refresh();
3191
+ const projection = await this.getCurrent();
3192
+ for (const subscription of this.subscriptions) subscription.replacePolicy(projection.policy);
3193
+ return projection;
3194
+ }
3195
+ /** Retire retained Environment and physical projection work. */
3196
+ async dispose() {
3197
+ if (this.disposed) return;
3198
+ this.disposed = true;
3199
+ for (const subscription of this.subscriptions) subscription.unsubscribe();
3200
+ this.subscriptions.clear();
3201
+ await this.observationStartPromise?.catch(() => {});
3202
+ const releases = this.observationReleases;
3203
+ this.observationReleases = [];
3204
+ await Promise.all(releases.map((release) => release()));
3205
+ }
3206
+ assertActive() {
3207
+ if (this.disposed) throw new Error("Agent delivery projection service is disposed.");
3208
+ }
3209
+ ensurePhysicalObservation() {
3210
+ this.assertActive();
3211
+ if (this.observationStartPromise) return this.observationStartPromise;
3212
+ const startPromise = (async () => {
3213
+ const releases = [];
3214
+ try {
3215
+ releases.push(await this.options.observationEnvironment.acquireRoot(this.options.projectDir));
3216
+ releases.push(await this.options.observationEnvironment.acquireRoot(getExternalCodexCommandObservationRoot()));
3217
+ if (this.disposed) {
3218
+ await Promise.all(releases.map((release) => release()));
3219
+ return;
3220
+ }
3221
+ this.observationReleases = releases;
3222
+ } catch (cause) {
3223
+ await Promise.all(releases.map((release) => release()));
3224
+ throw cause;
3225
+ }
3226
+ })();
3227
+ this.observationStartPromise = startPromise;
3228
+ return startPromise;
3229
+ }
3230
+ };
3231
+
2948
3232
  //#endregion
2949
3233
  //#region src/projection-work/phase-trace.ts
2950
3234
  /**
@@ -5757,16 +6041,17 @@ function parseOpsxConfigDrift(output) {
5757
6041
  //#endregion
5758
6042
  //#region src/planning-config-service.ts
5759
6043
  /**
5760
- * Orthogonal intents (updated 2026-07-27 Asia/Shanghai):
5761
- * 1. Read launch-project binding and active-root config from physically distinct roots.
6044
+ * Orthogonal intents (updated 2026-08-02 Asia/Shanghai):
6045
+ * 1. Read launch-project binding and reusable YAML source from its physical owner.
5762
6046
  * 2. Project environment-global config, profile, and drift through one CLI-owned reactive read.
5763
- * 3. Mutate only the explicitly selected ownership facet and refresh reactive caches.
6047
+ * 3. Mutate Project Binding or Environment Global only and refresh reactive caches.
5764
6048
  * 4. Return launch-file write evidence without waiting for Planning-root service convergence.
5765
- * 5. Keep editable Environment Global file bytes in a file-native owner separate from CLI Work.
6049
+ * 5. Keep configured Environment defaults separate from effective Root Context and file bytes.
5766
6050
  *
5767
6051
  * Original request (2026-07-15): "Config ownership separates launch-project binding, active-root config, and environment-global config."
5768
6052
  * Original request (2026-07-18): "Profile/Drift must refresh with external environment config changes."
5769
6053
  * Derived requirement (2026-07-19): "Project Binding mutation uses write-then-converge settlement."
6054
+ * Derived requirement (2026-08-02): "Active Root revision-aware mutation moves to its own physical service."
5770
6055
  */
5771
6056
  function projectEnvironmentProfileState(config, configError, driftEvidence) {
5772
6057
  if (!config) return {
@@ -5798,7 +6083,8 @@ function projectEnvironmentProfileState(config, configError, driftEvidence) {
5798
6083
  warningText: drift.warningText
5799
6084
  };
5800
6085
  }
5801
- async function readProjectConfigFile(options) {
6086
+ /** Read the preferred config.yaml/config.yml source under one physical project or Store root. */
6087
+ async function readPlanningYamlConfigFile(options) {
5802
6088
  const yamlPath = join(options.rootPath, "openspec", "config.yaml");
5803
6089
  const yaml = await reactiveReadFile(yamlPath);
5804
6090
  if (yaml !== null) return {
@@ -5824,7 +6110,7 @@ async function readProjectConfigFile(options) {
5824
6110
  }
5825
6111
  /** Read project-owned Store and Reference declarations from the launch project. */
5826
6112
  async function readProjectBindingConfig(input) {
5827
- const file = await readProjectConfigFile({ rootPath: input.launchProjectDir });
6113
+ const file = await readPlanningYamlConfigFile({ rootPath: input.launchProjectDir });
5828
6114
  return {
5829
6115
  kind: "project-binding",
5830
6116
  owner: {
@@ -5836,22 +6122,6 @@ async function readProjectBindingConfig(input) {
5836
6122
  rootPreview: input.rootPreview
5837
6123
  };
5838
6124
  }
5839
- /** Read configuration owned by the currently selected writable Planning root. */
5840
- async function readActiveRootConfig(input) {
5841
- const planningRoot = input.rootContext.planningRoot;
5842
- if (!planningRoot) throw new Error("Planning root is unavailable.");
5843
- return {
5844
- kind: "active-root",
5845
- owner: {
5846
- kind: "planning-root",
5847
- path: planningRoot.path,
5848
- source: planningRoot.source,
5849
- storeId: input.rootContext.storeId,
5850
- externalToLaunchProject: planningRoot.path !== input.launchProjectDir
5851
- },
5852
- file: await readProjectConfigFile({ rootPath: planningRoot.path })
5853
- };
5854
- }
5855
6125
  /** Resolve and read the CLI-selected environment-global configuration file. */
5856
6126
  async function readEnvironmentGlobalConfig(input) {
5857
6127
  const [pathEvidence, configResult, driftEvidence] = await Promise.all([
@@ -5874,6 +6144,7 @@ async function readEnvironmentGlobalConfig(input) {
5874
6144
  },
5875
6145
  configPath,
5876
6146
  config: configEvidence.data,
6147
+ defaultStore: inspectEnvironmentDefaultStore(configEvidence.data),
5877
6148
  profileState: projectEnvironmentProfileState(configEvidence.data, configEvidence.contractError ?? (configEvidence.success ? void 0 : configEvidence.stderr || "Failed to load profile config."), driftEvidence),
5878
6149
  evidence: {
5879
6150
  path: pathEvidence,
@@ -5906,14 +6177,14 @@ async function writeConfigFile(path, content) {
5906
6177
  }
5907
6178
  /** Update only project binding fields in the launch-project configuration. */
5908
6179
  async function writeProjectBindingConfig(input) {
5909
- const file = await readProjectConfigFile({ rootPath: input.launchProjectDir });
6180
+ const file = await readPlanningYamlConfigFile({ rootPath: input.launchProjectDir });
5910
6181
  if (!file.path) throw new Error("Launch-project config path is unavailable.");
5911
6182
  await writePhysicalReactiveFile({
5912
6183
  rootPath: input.launchProjectDir,
5913
6184
  relativePath: join("openspec", file.format === "yml" ? "config.yml" : "config.yaml"),
5914
6185
  content: updateProjectBindingContent(file.content, input.update)
5915
6186
  });
5916
- const writtenFile = await readProjectConfigFile({ rootPath: input.launchProjectDir });
6187
+ const writtenFile = await readPlanningYamlConfigFile({ rootPath: input.launchProjectDir });
5917
6188
  return {
5918
6189
  state: "write-complete",
5919
6190
  owner: {
@@ -5925,12 +6196,6 @@ async function writeProjectBindingConfig(input) {
5925
6196
  completedAt: Date.now()
5926
6197
  };
5927
6198
  }
5928
- /** Replace the active Planning-root configuration through its reactive file owner. */
5929
- async function writeActiveRootConfig(input) {
5930
- const active = await readActiveRootConfig(input);
5931
- if (!active.file.path) throw new Error("Active-root config path is unavailable.");
5932
- await writeConfigFile(active.file.path, input.content);
5933
- }
5934
6199
  /** Replace the CLI-selected environment-global configuration document. */
5935
6200
  async function writeEnvironmentGlobalConfig(input) {
5936
6201
  const pathEvidence = await input.cliExecutor.execute(["config", "path"]);
@@ -5939,17 +6204,33 @@ async function writeEnvironmentGlobalConfig(input) {
5939
6204
  if (!configPath) throw new Error("OpenSpec global config path is empty.");
5940
6205
  await writeConfigFile(configPath, `${JSON.stringify(input.config, null, 2)}\n`);
5941
6206
  }
6207
+ /** Set or explicitly clear only the official machine `defaultStore` field through the CLI. */
6208
+ async function writeEnvironmentDefaultStore(input) {
6209
+ const args = input.update.value ? [
6210
+ "config",
6211
+ "set",
6212
+ "defaultStore",
6213
+ input.update.value,
6214
+ "--string"
6215
+ ] : [
6216
+ "config",
6217
+ "unset",
6218
+ "defaultStore"
6219
+ ];
6220
+ const result = await input.cliExecutor.execute(args);
6221
+ if (!result.success) throw new Error(result.stderr || `OpenSpec ${args.slice(0, 3).join(" ")} failed.`);
6222
+ return result;
6223
+ }
5942
6224
 
5943
6225
  //#endregion
5944
6226
  //#region src/environment-global-projection-service.ts
5945
6227
  /**
5946
- * Orthogonal intents (created 2026-07-26 Asia/Shanghai):
5947
- * 1. Own Environment Global CLI truth at the Server runtime-environment boundary.
6228
+ * Orthogonal intents (updated 2026-08-01 Asia/Shanghai):
6229
+ * 1. Own Environment Global CLI truth, including configured `defaultStore`, at runtime scope.
5948
6230
  * 2. Track the CLI-resolved config path as a dynamic reactive dependency.
5949
6231
  * 3. Expose immediate Pull, lifecycle-only Push, explicit refresh, and settled compatibility reads.
5950
- * 4. Keep runtime-environment Work independent from Planning-root replacement.
5951
- * 5. Own and retire the observed physical config-path lease for the active CLI result.
5952
- * 6. Keep editable file bytes in a separate file-native Projection Work owner.
6232
+ * 4. Keep editable file bytes physically separate from CLI projection Work.
6233
+ * 5. Retire path leases and refresh Environment plus Root only after physical config replacement settles.
5953
6234
  *
5954
6235
  * Original request (2026-07-26): "展开全面的接口升级和内核升级和测试升级。"
5955
6236
  */
@@ -5968,9 +6249,15 @@ var EnvironmentGlobalProjectionService = class {
5968
6249
  configPathGeneration = 0;
5969
6250
  disposed = false;
5970
6251
  fileBridgeSubscription = null;
6252
+ configFileSettlementSubscription = null;
6253
+ configFileSettlementGeneration = 0;
5971
6254
  constructor(options) {
5972
6255
  this.options = options;
5973
6256
  }
6257
+ /** Keep machine-config observation alive even when no Config page is mounted. */
6258
+ start() {
6259
+ this.ensureFileBridge();
6260
+ }
5974
6261
  /** Retire the dynamic config-path lease before the Server observation environment closes. */
5975
6262
  async dispose() {
5976
6263
  if (this.disposed) return;
@@ -5979,20 +6266,24 @@ var EnvironmentGlobalProjectionService = class {
5979
6266
  this.configPathObservation = null;
5980
6267
  this.fileBridgeSubscription?.unsubscribe();
5981
6268
  this.fileBridgeSubscription = null;
6269
+ this.configFileSettlementSubscription?.unsubscribe();
6270
+ this.configFileSettlementSubscription = null;
5982
6271
  await observation?.release();
5983
6272
  }
5984
6273
  read() {
5985
6274
  return this.options.workOwner.registry.read(this.identity()) ?? this.loadingState();
5986
6275
  }
5987
6276
  refresh() {
5988
- this.options.workOwner.registry.invalidate(this.identity());
5989
6277
  this.ensureFileBridge();
6278
+ this.options.workOwner.registry.invalidate(this.identity());
5990
6279
  return this.read();
5991
6280
  }
5992
6281
  subscribe(listener) {
6282
+ this.ensureFileBridge();
5993
6283
  return this.options.workOwner.registry.subscribeLifecycle(this.request(), listener);
5994
6284
  }
5995
6285
  getCurrent() {
6286
+ this.ensureFileBridge();
5996
6287
  return new Promise((resolve$1, reject) => {
5997
6288
  let settled = false;
5998
6289
  let subscription = null;
@@ -6078,7 +6369,7 @@ var EnvironmentGlobalProjectionService = class {
6078
6369
  gitBindingToken: null
6079
6370
  },
6080
6371
  selector: "config+profile+drift",
6081
- inputFingerprint: "openspec-cli-1.6:config-path+list-json+list",
6372
+ inputFingerprint: "openspec-cli-1.7:config-path+list-json+list+default-store",
6082
6373
  protocolVersion: 1
6083
6374
  };
6084
6375
  }
@@ -6113,7 +6404,7 @@ var EnvironmentGlobalProjectionService = class {
6113
6404
  gitBindingToken: null
6114
6405
  },
6115
6406
  selector: "config-file",
6116
- inputFingerprint: "openspec-cli-1.6:config-path+file-bytes",
6407
+ inputFingerprint: "openspec-cli-1.7:config-path+file-bytes",
6117
6408
  protocolVersion: 1
6118
6409
  };
6119
6410
  }
@@ -6157,6 +6448,12 @@ var EnvironmentGlobalProjectionService = class {
6157
6448
  if (!cliState?.data || !fileState?.data || nextPath === currentPath) return;
6158
6449
  this.options.workOwner.fileRegistry.invalidate(this.fileIdentity(), "dependency");
6159
6450
  });
6451
+ this.configFileSettlementSubscription = this.options.workOwner.fileRegistry.subscribeLifecycle(this.fileRequest(), (notice) => {
6452
+ if (notice.invalidationCause !== "dependency" || notice.state !== "ready" || notice.workGeneration <= this.configFileSettlementGeneration) return;
6453
+ this.configFileSettlementGeneration = notice.workGeneration;
6454
+ this.options.workOwner.registry.invalidate(this.identity(), "dependency");
6455
+ this.options.onConfigFileSettled?.();
6456
+ });
6160
6457
  }
6161
6458
  loadingState() {
6162
6459
  return {
@@ -8298,11 +8595,12 @@ async function readSpecDocument(source, identity) {
8298
8595
  //#endregion
8299
8596
  //#region src/planning-cli-projection-service.ts
8300
8597
  /**
8301
- * Orthogonal intents (updated 2026-07-27 Asia/Shanghai):
8302
- * 1. Own selector-exact OPSX and Spec Projection Work, including Reference-content invalidation, for one Planning-root record.
8598
+ * Orthogonal intents (updated 2026-08-02 Asia/Shanghai):
8599
+ * 1. Own selector-exact OPSX Status/Instructions and Spec Projection Work for one Planning-root record.
8303
8600
  * 2. Execute typed Kernel/CLI readers against one reactive Manager-owned Root Context value per Work generation.
8304
8601
  * 3. Expose immediate Pull, lifecycle-only Push, explicit refresh, and settled compatibility reads.
8305
8602
  * 4. Share one bounded typed registry across replaceable Planning-root records.
8603
+ * 5. Retire every config-dependent CLI Work generation before Active Root mutation settlement escapes its lease.
8306
8604
  *
8307
8605
  * Original request (2026-07-26): "展开全面的接口升级和内核升级和测试升级。"
8308
8606
  * Owner architecture clarification (2026-07-26): "将这些变更信息收集起来作为触发器,更新底层幂等计算的缓存结果。"
@@ -8345,6 +8643,14 @@ var PlanningCliProjectionService = class {
8345
8643
  this.options.workOwner.registry.invalidate(this.identity(selector));
8346
8644
  return this.read(selector);
8347
8645
  }
8646
+ /** Retire every current-root CLI Work item whose result depends on Active Root configuration. */
8647
+ invalidateConfigDependentWork() {
8648
+ const rootContext = this.currentRootContext();
8649
+ const planningRoot = rootContext.planningRoot;
8650
+ if (!planningRoot) return;
8651
+ const generation = rootContext.generation ?? this.options.gitBindingToken;
8652
+ this.options.workOwner.registry.invalidateMatching((identity) => identity.projectionKind === "planning-cli" && identity.planningRoot.identity === planningRoot.path && identity.owner.generation === generation && (identity.selector === "opsx-status" || identity.selector === "opsx-change-list" || identity.selector === "opsx-status-list" || identity.selector === "opsx-instructions" || identity.selector === "opsx-apply-instructions" || identity.selector === "opsx-archive-instructions" || identity.selector === "opsx-config-bundle" || identity.selector === "opsx-templates" || identity.selector === "opsx-template-contents"), "dependency");
8653
+ }
8348
8654
  subscribe(rawSelector, listener) {
8349
8655
  const selector = this.selector(rawSelector);
8350
8656
  return this.options.workOwner.registry.subscribeLifecycle(this.request(selector), listener);
@@ -8400,6 +8706,9 @@ var PlanningCliProjectionService = class {
8400
8706
  })).with({ kind: "opsx-apply-instructions" }, (value) => ({
8401
8707
  ...value,
8402
8708
  change: requireCanonicalOpenSpecEntityId(value.change, "changeId")
8709
+ })).with({ kind: "opsx-archive-instructions" }, (value) => ({
8710
+ ...value,
8711
+ change: requireCanonicalOpenSpecEntityId(value.change, "changeId")
8403
8712
  })).otherwise((value) => value);
8404
8713
  }
8405
8714
  identity(selector) {
@@ -8439,7 +8748,7 @@ var PlanningCliProjectionService = class {
8439
8748
  };
8440
8749
  }
8441
8750
  trackDependencies(selector) {
8442
- match(selector).with({ kind: "spec-catalog" }, { kind: "spec-document" }, () => {}).with({ kind: "opsx-status" }, { kind: "opsx-change-list" }, { kind: "opsx-status-list" }, () => this.options.invalidation.track("schemas")).with({ kind: "opsx-instructions" }, { kind: "opsx-apply-instructions" }, () => this.options.invalidation.track("schemas")).with({ kind: "opsx-config-bundle" }, { kind: "opsx-templates" }, { kind: "opsx-template-contents" }, () => this.options.invalidation.track("schemas")).exhaustive();
8751
+ match(selector).with({ kind: "spec-catalog" }, { kind: "spec-document" }, () => {}).with({ kind: "opsx-status" }, { kind: "opsx-change-list" }, { kind: "opsx-status-list" }, () => this.options.invalidation.track("schemas")).with({ kind: "opsx-instructions" }, { kind: "opsx-apply-instructions" }, { kind: "opsx-archive-instructions" }, () => this.options.invalidation.track("schemas")).with({ kind: "opsx-config-bundle" }, { kind: "opsx-templates" }, { kind: "opsx-template-contents" }, () => this.options.invalidation.track("schemas")).exhaustive();
8443
8752
  }
8444
8753
  invalidateSpecObservation(change) {
8445
8754
  if (change.kind === "doctor-root") return;
@@ -8449,7 +8758,7 @@ var PlanningCliProjectionService = class {
8449
8758
  const planningRoot = rootContext.planningRoot;
8450
8759
  if (!planningRoot) return;
8451
8760
  const generation = rootContext.generation ?? this.options.gitBindingToken;
8452
- this.options.workOwner.registry.invalidateMatching((identity) => identity.projectionKind === "planning-cli" && identity.planningRoot.identity === planningRoot.path && identity.owner.generation === generation && (identity.selector === "spec-catalog" || identity.selector === "spec-document" || identity.selector === "opsx-instructions" || identity.selector === "opsx-apply-instructions"), "dependency");
8761
+ this.options.workOwner.registry.invalidateMatching((identity) => identity.projectionKind === "planning-cli" && identity.planningRoot.identity === planningRoot.path && identity.owner.generation === generation && (identity.selector === "spec-catalog" || identity.selector === "spec-document" || identity.selector === "opsx-instructions" || identity.selector === "opsx-apply-instructions" || identity.selector === "opsx-archive-instructions"), "dependency");
8453
8762
  }
8454
8763
  currentRootContext() {
8455
8764
  return this.options.rootContext instanceof PlanningCliRootContextState ? this.options.rootContext.get() : this.options.rootContext;
@@ -8475,6 +8784,10 @@ var PlanningCliProjectionService = class {
8475
8784
  })).with({ kind: "opsx-apply-instructions" }, async ({ change, schema }) => ({
8476
8785
  kind: "opsx-apply-instructions",
8477
8786
  value: await this.options.kernel.readApplyInstructionsProjection(change, schema)
8787
+ })).with({ kind: "opsx-archive-instructions" }, async ({ change, schema }) => ({
8788
+ kind: "opsx-archive-instructions",
8789
+ rootGeneration: rootContext.generation ?? this.options.gitBindingToken,
8790
+ value: await this.options.kernel.readArchiveInstructionsProjection(change, schema)
8478
8791
  })).with({ kind: "opsx-config-bundle" }, async () => ({
8479
8792
  kind: "opsx-config-bundle",
8480
8793
  ...await this.options.kernel.readConfigBundleProjection()
@@ -9983,7 +10296,7 @@ async function collectSearchDocuments(adapter, documentService, resolveEntityRea
9983
10296
  scope: "active-root",
9984
10297
  title: spec.name,
9985
10298
  href: specRoutePath(identity),
9986
- path: `owned:openspec/specs/${spec.id}/spec.md`,
10299
+ path: specDocumentDisplayPath(identity),
9987
10300
  content: typeof raw === "string" ? raw : raw.markdown,
9988
10301
  updatedAt: spec.updatedAt
9989
10302
  });
@@ -9994,7 +10307,7 @@ async function collectSearchDocuments(adapter, documentService, resolveEntityRea
9994
10307
  scope: "referenced-specs",
9995
10308
  title: spec.name,
9996
10309
  href: specRoutePath(spec.identity),
9997
- path: `referenced:${spec.identity.storeId}:specs/${spec.identity.specId}`,
10310
+ path: specDocumentDisplayPath(spec.identity),
9998
10311
  content: `Requirement count: ${spec.requirementCount}`,
9999
10312
  updatedAt: 0
10000
10313
  });
@@ -10168,10 +10481,10 @@ var SearchService = class {
10168
10481
  //#endregion
10169
10482
  //#region src/workflow-invocation-service.ts
10170
10483
  /**
10171
- * Orthogonal intents (updated 2026-07-20 Asia/Shanghai):
10484
+ * Orthogonal intents (updated 2026-08-01 Asia/Shanghai):
10172
10485
  * 1. Resolve OPSX compose/command/direct invocation modes.
10173
10486
  * 2. Bind every invocation to the CLI-selected Root Context and Store selector.
10174
- * 3. Preserve command-specific Status/Instructions evidence through hooks and clients.
10487
+ * 3. Preserve command-specific Status, Apply, Artifact, and Archive Instructions evidence through hooks and clients.
10175
10488
  * 4. Generate Agent/CLI payloads without reconstructing planning paths.
10176
10489
  * 5. Return Manager-owned root generation and target evidence for stale-dispatch guards.
10177
10490
  *
@@ -10313,9 +10626,9 @@ function buildArchivePrompt(changeId, evidence, target) {
10313
10626
  "",
10314
10627
  ...targetContextLines(target),
10315
10628
  "",
10316
- "CLI-owned workflow-status evidence:",
10629
+ "CLI-owned archive-instructions evidence:",
10317
10630
  "```json",
10318
- normalized.length > 0 ? normalized : "(no status output)",
10631
+ normalized.length > 0 ? normalized : "(no archive-instructions output)",
10319
10632
  "```",
10320
10633
  "",
10321
10634
  "Please confirm archive readiness, highlight risks, and provide the exact next steps."
@@ -10468,12 +10781,16 @@ var WorkflowInvocationService = class {
10468
10781
  };
10469
10782
  case "update":
10470
10783
  case "verify":
10471
- case "sync":
10472
- case "archive": return {
10784
+ case "sync": return {
10473
10785
  kind: "workflow-status",
10474
10786
  options,
10475
10787
  result: await this.options.contracts.workflowStatus(input.changeId, options)
10476
10788
  };
10789
+ case "archive": return {
10790
+ kind: "archive-instructions",
10791
+ options,
10792
+ result: await this.options.contracts.archiveInstructions(input.changeId, options)
10793
+ };
10477
10794
  case "explore":
10478
10795
  case "propose":
10479
10796
  case "new":
@@ -12453,7 +12770,7 @@ var RootContextProjectionService = class {
12453
12770
  gitBindingToken: null
12454
12771
  },
12455
12772
  selector: JSON.stringify({ dataScopePath: this.options.dataScopePath }),
12456
- inputFingerprint: "openspec-cli-1.6:doctor+context",
12773
+ inputFingerprint: "openspec-cli-1.7:doctor+context+global-default",
12457
12774
  protocolVersion: 1
12458
12775
  };
12459
12776
  }
@@ -12478,6 +12795,131 @@ var RootContextProjectionService = class {
12478
12795
  }
12479
12796
  };
12480
12797
 
12798
+ //#endregion
12799
+ //#region src/active-root-config-service.ts
12800
+ /**
12801
+ * Orthogonal intents (updated 2026-08-02 Asia/Shanghai):
12802
+ * 1. Project exact Active Root owner, file, official fields, diagnostics, and revision.
12803
+ * 2. Serialize in-process mutations by physical config path and compare loaded ownership evidence.
12804
+ * 3. Apply Structured node patches or Raw whole-document syntax validation.
12805
+ * 4. Convert atomic physical compare/write outcomes into typed latest-source recovery results.
12806
+ *
12807
+ * Original request (2026-08-01): preserve raw YAML writes for team-defined keys outside the official standard.
12808
+ * Derived checkpoint (2026-08-02): parallel and external stale saves return conflicts with latest physical source.
12809
+ */
12810
+ const mutationTails = /* @__PURE__ */ new Map();
12811
+ function createRevision(config) {
12812
+ const source = JSON.stringify({
12813
+ owner: config.owner,
12814
+ file: {
12815
+ path: config.file.path,
12816
+ exists: config.file.exists,
12817
+ content: config.file.content
12818
+ }
12819
+ });
12820
+ return ActiveRootRevisionSchema.parse(`sha256:${createHash("sha256").update(source).digest("hex")}`);
12821
+ }
12822
+ async function serializeMutation(path, mutation) {
12823
+ const execution = (mutationTails.get(path) ?? Promise.resolve()).catch(() => void 0).then(mutation);
12824
+ const tail = execution.then(() => void 0, () => void 0);
12825
+ mutationTails.set(path, tail);
12826
+ try {
12827
+ return await execution;
12828
+ } finally {
12829
+ if (mutationTails.get(path) === tail) mutationTails.delete(path);
12830
+ }
12831
+ }
12832
+ /** Read the exact active Planning-root YAML source and derive its opaque loaded revision. */
12833
+ async function readActiveRootConfig(input) {
12834
+ const planningRoot = input.rootContext.planningRoot;
12835
+ if (!planningRoot) throw new Error("Planning root is unavailable.");
12836
+ const file = await readPlanningYamlConfigFile({ rootPath: planningRoot.path });
12837
+ const inspection = inspectActiveRootOfficialConfig(file.content);
12838
+ const configWithoutRevision = {
12839
+ kind: "active-root",
12840
+ owner: {
12841
+ kind: "planning-root",
12842
+ path: planningRoot.path,
12843
+ source: planningRoot.source,
12844
+ storeId: input.rootContext.storeId,
12845
+ externalToLaunchProject: planningRoot.path !== input.launchProjectDir
12846
+ },
12847
+ file,
12848
+ ...inspection
12849
+ };
12850
+ return {
12851
+ ...configWithoutRevision,
12852
+ revision: createRevision(configWithoutRevision)
12853
+ };
12854
+ }
12855
+ function conflictReason(latest, mutation) {
12856
+ if (latest.owner.path !== mutation.ownerPath) return "owner-changed";
12857
+ if (latest.file.path !== mutation.filePath) return "file-changed";
12858
+ if (latest.revision !== mutation.revision) return "revision-changed";
12859
+ return null;
12860
+ }
12861
+ function structuredSourceDiagnostics(latest) {
12862
+ const sourceDiagnostics = latest.diagnostics.filter(({ code }) => code === "config-unparseable" || code === "config-not-mapping");
12863
+ return sourceDiagnostics.length > 0 ? sourceDiagnostics : [{
12864
+ code: "config-not-mapping",
12865
+ severity: "error",
12866
+ path: "$",
12867
+ message: "The current Active Root source cannot be edited in Structured mode."
12868
+ }];
12869
+ }
12870
+ /**
12871
+ * Apply one exact-revision Structured or Raw mutation through atomic physical compare-and-write.
12872
+ * Conflicts and invalid sources retain the latest authoritative projection for explicit recovery.
12873
+ */
12874
+ async function mutateActiveRootConfig(input) {
12875
+ const mutation = ActiveRootMutationSchema.parse(input.mutation);
12876
+ return serializeMutation((await readActiveRootConfig(input)).file.path, async () => {
12877
+ const latest = await readActiveRootConfig(input);
12878
+ const reason = conflictReason(latest, mutation);
12879
+ if (reason) return {
12880
+ state: "conflict",
12881
+ reason,
12882
+ latest
12883
+ };
12884
+ let content;
12885
+ if (mutation.mode === "raw") {
12886
+ const validation = validateActiveRootRawYaml(mutation.content);
12887
+ if (!validation.valid) return {
12888
+ state: "invalid",
12889
+ reason: "raw-syntax",
12890
+ diagnostics: validation.diagnostics,
12891
+ latest
12892
+ };
12893
+ content = mutation.content;
12894
+ } else try {
12895
+ content = patchActiveRootOfficialFields(latest.file.content, mutation.update);
12896
+ } catch {
12897
+ return {
12898
+ state: "invalid",
12899
+ reason: "structured-source",
12900
+ diagnostics: structuredSourceDiagnostics(latest),
12901
+ latest
12902
+ };
12903
+ }
12904
+ const write$1 = await compareAndWriteAtomicPhysicalReactiveFile({
12905
+ rootPath: latest.owner.path,
12906
+ relativePath: relative(latest.owner.path, latest.file.path),
12907
+ expectedContent: latest.file.content,
12908
+ content
12909
+ });
12910
+ const settled = await readActiveRootConfig(input);
12911
+ if (write$1.state === "conflict") return {
12912
+ state: "conflict",
12913
+ reason: "revision-changed",
12914
+ latest: settled
12915
+ };
12916
+ return {
12917
+ state: "applied",
12918
+ config: settled
12919
+ };
12920
+ });
12921
+ }
12922
+
12481
12923
  //#endregion
12482
12924
  //#region src/cli-mutation-invalidator.ts
12483
12925
  /** Settlement boundary shared by buffered and streaming CLI mutation execution. */
@@ -13322,6 +13764,7 @@ function getOpenSpecMutationFacets(args) {
13322
13764
  if (args.includes("--help") || args.includes("-h")) return null;
13323
13765
  const command = findCommand(args);
13324
13766
  if (!command) return null;
13767
+ if (command === "init") return PROJECT_SCHEMA_FACETS;
13325
13768
  if (PROJECT_MUTATION_COMMANDS.has(command)) return PROJECT_CONTEXT_FACETS;
13326
13769
  const subcommand = findSubcommand(args.slice(args.indexOf(command) + 1), command === "config" ? new Set(["--scope"]) : void 0);
13327
13770
  if (command === "schema" && subcommand && SCHEMA_MUTATION_COMMANDS.has(subcommand)) return PROJECT_SCHEMA_FACETS;
@@ -13331,6 +13774,146 @@ function getOpenSpecMutationFacets(args) {
13331
13774
  return null;
13332
13775
  }
13333
13776
 
13777
+ //#endregion
13778
+ //#region src/project-initialization-operation.ts
13779
+ function createDeferred() {
13780
+ let resolve$1;
13781
+ let reject;
13782
+ return {
13783
+ promise: new Promise((promiseResolve, promiseReject) => {
13784
+ resolve$1 = promiseResolve;
13785
+ reject = promiseReject;
13786
+ }),
13787
+ resolve: resolve$1,
13788
+ reject
13789
+ };
13790
+ }
13791
+ /** Server-local single-operation owner for Launch Project initialization. */
13792
+ var ProjectInitializationOperationOwner = class {
13793
+ active = null;
13794
+ records = /* @__PURE__ */ new Map();
13795
+ start(input) {
13796
+ const existing = this.records.get(input.requestId);
13797
+ if (existing) {
13798
+ for (const event of existing.events) input.onEvent(event);
13799
+ if (this.active === existing) existing.listeners.add(input.onEvent);
13800
+ return existing.handle;
13801
+ }
13802
+ if (this.active) throw new Error(`Project initialization ${this.active.requestId} is already running.`);
13803
+ const terminal = createDeferred();
13804
+ let successSettlementStarted = false;
13805
+ let cancelRequested = false;
13806
+ let rawHandle = null;
13807
+ let record;
13808
+ const emit = (event) => {
13809
+ record.events.push(event);
13810
+ for (const listener of record.listeners) listener(event);
13811
+ };
13812
+ const settleSuccess = (event) => {
13813
+ if (successSettlementStarted) return;
13814
+ successSettlementStarted = true;
13815
+ input.settleSuccess().then(() => {
13816
+ emit(event);
13817
+ terminal.resolve({
13818
+ reason: "exited",
13819
+ exitCode: 0
13820
+ });
13821
+ }, (error) => {
13822
+ emit({
13823
+ type: "stderr",
13824
+ data: error instanceof Error ? error.message : String(error)
13825
+ });
13826
+ emit({
13827
+ type: "exit",
13828
+ exitCode: 1
13829
+ });
13830
+ terminal.resolve({
13831
+ reason: "exited",
13832
+ exitCode: 1
13833
+ });
13834
+ });
13835
+ };
13836
+ const handle = {
13837
+ settled: terminal.promise,
13838
+ cancel: () => {
13839
+ if (!cancelRequested) {
13840
+ cancelRequested = true;
13841
+ if (rawHandle) rawHandle.cancel().catch((error) => terminal.reject(error));
13842
+ else terminal.resolve({
13843
+ reason: "cancelled",
13844
+ exitCode: null
13845
+ });
13846
+ }
13847
+ return terminal.promise;
13848
+ }
13849
+ };
13850
+ record = {
13851
+ requestId: input.requestId,
13852
+ handle,
13853
+ events: [],
13854
+ listeners: new Set([input.onEvent])
13855
+ };
13856
+ this.active = record;
13857
+ this.retain(record);
13858
+ try {
13859
+ rawHandle = input.start((event) => {
13860
+ if (event.type === "exit" && event.exitCode === 0) {
13861
+ settleSuccess(event);
13862
+ return;
13863
+ }
13864
+ emit(event);
13865
+ });
13866
+ } catch (error) {
13867
+ terminal.reject(error);
13868
+ throw error;
13869
+ }
13870
+ rawHandle.settled.then((settlement) => {
13871
+ if (settlement.reason === "exited" && settlement.exitCode === 0) return;
13872
+ terminal.resolve(settlement);
13873
+ }, (error) => terminal.reject(error));
13874
+ terminal.promise.finally(() => {
13875
+ if (this.active === record) this.active = null;
13876
+ }).catch(() => {});
13877
+ return handle;
13878
+ }
13879
+ cancel(requestId) {
13880
+ const existing = this.records.get(requestId);
13881
+ if (existing) return existing.handle.cancel();
13882
+ const settlement = {
13883
+ reason: "cancelled",
13884
+ exitCode: null
13885
+ };
13886
+ const handle = {
13887
+ settled: Promise.resolve(settlement),
13888
+ cancel: () => Promise.resolve(settlement)
13889
+ };
13890
+ this.retain({
13891
+ requestId,
13892
+ handle,
13893
+ events: [{
13894
+ type: "exit",
13895
+ exitCode: null
13896
+ }],
13897
+ listeners: /* @__PURE__ */ new Set()
13898
+ });
13899
+ return Promise.resolve(settlement);
13900
+ }
13901
+ retain(record) {
13902
+ this.records.set(record.requestId, record);
13903
+ while (this.records.size > 256) {
13904
+ const oldestRequestId = this.records.keys().next().value;
13905
+ if (!oldestRequestId) return;
13906
+ const oldest = this.records.get(oldestRequestId);
13907
+ if (oldest === this.active) {
13908
+ this.records.delete(oldestRequestId);
13909
+ this.records.set(oldestRequestId, oldest);
13910
+ continue;
13911
+ }
13912
+ this.records.delete(oldestRequestId);
13913
+ }
13914
+ }
13915
+ };
13916
+
13334
13917
  //#endregion
13335
13918
  //#region src/reactive-kv.ts
13336
13919
  /**
@@ -14147,6 +14730,37 @@ function streamOpenSpecCliMutation(ctx, args, start, onEvent) {
14147
14730
  if (!facets) return start(onEvent);
14148
14731
  return new CliMutationInvalidator(ctx.runtimeInvalidation).stream(facets, start, onEvent);
14149
14732
  }
14733
+ const projectInitializationOwners = /* @__PURE__ */ new WeakMap();
14734
+ const projectInitializationInput = z.object({ requestId: z.string().min(1).max(128) });
14735
+ function projectInitializationOwner(ctx) {
14736
+ const existing = projectInitializationOwners.get(ctx.cliExecutor);
14737
+ if (existing) return existing;
14738
+ const owner = new ProjectInitializationOperationOwner();
14739
+ projectInitializationOwners.set(ctx.cliExecutor, owner);
14740
+ return owner;
14741
+ }
14742
+ async function waitForLaunchProjectInitialization(ctx) {
14743
+ const timeoutAt = Date.now() + 5e3;
14744
+ while (Date.now() < timeoutAt) {
14745
+ if ((await ctx.launchProjectAdapter.readLaunchProjectInitialization()).initialized) return;
14746
+ await new Promise((resolve$1) => setTimeout(resolve$1, 25));
14747
+ }
14748
+ throw new Error("OpenSpec CLI exited successfully, but the Launch Project openspec/ directory did not settle.");
14749
+ }
14750
+ async function settleProjectInitializationReplacements(ctx) {
14751
+ ctx.rootContextProjectionService.refresh();
14752
+ await Promise.all([waitForLaunchProjectInitialization(ctx), ctx.rootContextProjectionService.getCurrent()]);
14753
+ await Promise.all([
14754
+ fetchProjectBindingConfig(ctx),
14755
+ fetchActiveRootConfig(ctx),
14756
+ runPlanningRootRead(ctx, async ({ planningCliProjectionService }) => {
14757
+ const projection = await planningCliProjectionService.getCurrent({ kind: "opsx-config-bundle" });
14758
+ if (projection.kind !== "opsx-config-bundle") throw new Error("Unexpected OPSX Config-bundle projection after project initialization.");
14759
+ return projection.value;
14760
+ }),
14761
+ ctx.agentDeliveryProjectionService.refresh()
14762
+ ]);
14763
+ }
14150
14764
  /** Project-backend notification query, subscription, and mutation procedures. */
14151
14765
  const notificationsRouter = router({
14152
14766
  list: publicProcedure.query(({ ctx }) => {
@@ -14667,7 +15281,7 @@ const specRouter = router({
14667
15281
  identity: OwnedSpecIdentitySchema,
14668
15282
  content: z.string()
14669
15283
  })).mutation(async ({ ctx, input }) => {
14670
- const specId = requireCanonicalOpenSpecEntityId(input.identity.specId, "specId");
15284
+ const specId = requireCanonicalOpenSpecSpecId(input.identity.specId);
14671
15285
  await runPlanningRoot(ctx, ({ adapter }) => adapter.writeSpec(specId, input.content));
14672
15286
  return { success: true };
14673
15287
  }),
@@ -14756,10 +15370,34 @@ const changeRouter = router({
14756
15370
  /**
14757
15371
  * Init router - project initialization
14758
15372
  */
14759
- const initRouter = router({ init: publicProcedure.mutation(async ({ ctx }) => {
14760
- await ctx.launchProjectAdapter.init();
14761
- return { success: true };
14762
- }) });
15373
+ const initRouter = router({
15374
+ get: publicProcedure.query(({ ctx }) => ctx.launchProjectAdapter.readLaunchProjectInitialization()),
15375
+ subscribe: publicProcedure.subscription(({ ctx }) => {
15376
+ return observable((emit) => {
15377
+ const reactiveContext = new ReactiveContext();
15378
+ const controller = new AbortController();
15379
+ (async () => {
15380
+ try {
15381
+ for await (const state of reactiveContext.stream(() => ctx.launchProjectAdapter.readLaunchProjectInitialization(), controller.signal)) emit.next(state);
15382
+ } catch (error) {
15383
+ if (!controller.signal.aborted) emit.error(error instanceof Error ? error : new Error(String(error)));
15384
+ }
15385
+ })();
15386
+ return () => controller.abort();
15387
+ });
15388
+ }),
15389
+ initStream: publicProcedure.input(projectInitializationInput).subscription(({ ctx, input }) => {
15390
+ return createCliStreamObservable((onEvent) => {
15391
+ return projectInitializationOwner(ctx).start({
15392
+ requestId: input.requestId,
15393
+ onEvent,
15394
+ start: (operationEvent) => streamOpenSpecCliMutation(ctx, ["init"], (mutationEvent) => ctx.cliExecutor.initProjectStream(ctx.projectDir, mutationEvent), operationEvent),
15395
+ settleSuccess: () => settleProjectInitializationReplacements(ctx)
15396
+ });
15397
+ });
15398
+ }),
15399
+ cancel: publicProcedure.input(projectInitializationInput).mutation(({ ctx, input }) => projectInitializationOwner(ctx).cancel(input.requestId))
15400
+ });
14763
15401
  /**
14764
15402
  * Archive router - archived changes
14765
15403
  */
@@ -14943,13 +15581,6 @@ const configRouter = router({
14943
15581
  });
14944
15582
  })
14945
15583
  });
14946
- function buildPlanningRootUpdateArgs(rootContext, options = {}) {
14947
- const planningRoot = rootContext.planningRoot;
14948
- if (!planningRoot) throw new Error("Planning root is unavailable.");
14949
- const args = ["update", planningRoot.path];
14950
- if (options.force) args.push("--force");
14951
- return args;
14952
- }
14953
15584
  /**
14954
15585
  * CLI router - execute external openspec CLI commands
14955
15586
  */
@@ -14966,46 +15597,21 @@ const cliRouter = router({
14966
15597
  ], emitEvent), onEvent, () => ctx.configManager.invalidateResolvedCliRunner()));
14967
15598
  }),
14968
15599
  getAvailableTools: publicProcedure.query(() => {
14969
- return getAvailableTools().map((tool) => ({
14970
- name: tool.name,
14971
- value: tool.value,
14972
- available: tool.available,
14973
- successLabel: tool.successLabel
14974
- }));
15600
+ return getAvailableTools();
14975
15601
  }),
14976
15602
  getAllTools: publicProcedure.query(() => {
14977
- return getAllTools().map((tool) => ({
14978
- name: tool.name,
14979
- value: tool.value,
14980
- available: tool.available,
14981
- successLabel: tool.successLabel
14982
- }));
15603
+ return getAllTools();
14983
15604
  }),
14984
15605
  subscribeDetectedProjectTools: publicProcedure.subscription(({ ctx }) => {
14985
- return createReactiveSubscription(async () => {
14986
- return (await getDetectedProjectTools(ctx.projectDir)).map((tool) => ({
14987
- name: tool.name,
14988
- value: tool.value,
14989
- available: tool.available,
14990
- successLabel: tool.successLabel
14991
- }));
14992
- });
15606
+ return createReactiveSubscription(() => getDetectedProjectTools(ctx.projectDir));
14993
15607
  }),
14994
- subscribeToolInitStates: publicProcedure.input(z.object({
14995
- delivery: z.enum([
14996
- "both",
14997
- "skills",
14998
- "commands"
14999
- ]),
15000
- workflows: z.array(z.string()).default([])
15001
- })).subscription(({ ctx, input }) => {
15002
- const projectToolInitStates = createToolInitStateProjection(ctx.projectDir, {
15003
- delivery: input.delivery,
15004
- workflows: input.workflows
15005
- });
15006
- return createReactiveSubscription(async () => {
15007
- await ctx.toolCommandObservation.start();
15008
- return projectToolInitStates();
15608
+ subscribeToolInitStates: publicProcedure.subscription(({ ctx }) => {
15609
+ return observable((emit) => {
15610
+ const subscription = ctx.agentDeliveryProjectionService.subscribe((event) => {
15611
+ if (event.type === "snapshot") emit.next([...event.projection.states]);
15612
+ else emit.error(event.error);
15613
+ });
15614
+ return () => subscription.unsubscribe();
15009
15615
  });
15010
15616
  }),
15011
15617
  getConfiguredTools: publicProcedure.query(async ({ ctx }) => {
@@ -15014,21 +15620,6 @@ const cliRouter = router({
15014
15620
  subscribeConfiguredTools: publicProcedure.subscription(({ ctx }) => {
15015
15621
  return createReactiveSubscription(() => getConfiguredTools(ctx.projectDir));
15016
15622
  }),
15017
- init: publicProcedure.input(z.object({
15018
- tools: z.union([
15019
- z.array(z.string()),
15020
- z.literal("all"),
15021
- z.literal("none")
15022
- ]).optional(),
15023
- profile: z.enum(["core", "custom"]).optional(),
15024
- force: z.boolean().optional()
15025
- }).optional()).mutation(async ({ ctx, input }) => {
15026
- return runOpenSpecCliMutation(ctx, ["init"], () => ctx.cliExecutor.init({
15027
- tools: input?.tools,
15028
- profile: input?.profile,
15029
- force: input?.force
15030
- }));
15031
- }),
15032
15623
  validate: publicProcedure.input(z.discriminatedUnion("kind", [z.object({
15033
15624
  kind: z.literal("item"),
15034
15625
  id: z.string().min(1),
@@ -15074,41 +15665,19 @@ const cliRouter = router({
15074
15665
  }, onEvent);
15075
15666
  });
15076
15667
  }),
15077
- update: publicProcedure.input(z.object({ force: z.boolean().optional() }).optional()).mutation(async ({ ctx, input }) => {
15078
- return runPlanningRoot(ctx, ({ rootContext }) => {
15079
- const args = buildPlanningRootUpdateArgs(rootContext, input);
15080
- return runOpenSpecCliMutation(ctx, args, () => ctx.cliExecutor.execute(args));
15081
- });
15082
- }),
15083
- updateStream: publicProcedure.input(z.object({ force: z.boolean().optional() }).optional()).subscription(({ ctx, input }) => {
15084
- return createPlanningRootCliStreamObservable(ctx, ({ rootContext }, onEvent) => {
15085
- const args = buildPlanningRootUpdateArgs(rootContext, input);
15086
- return streamOpenSpecCliMutation(ctx, args, (mutationEvent) => ctx.cliExecutor.executeStream(args, mutationEvent), onEvent);
15087
- });
15088
- }),
15089
- initStream: publicProcedure.input(z.object({
15090
- tools: z.union([
15091
- z.array(z.string()),
15092
- z.literal("all"),
15093
- z.literal("none")
15094
- ]).optional(),
15095
- profile: z.enum(["core", "custom"]).optional(),
15096
- force: z.boolean().optional()
15097
- }).optional()).subscription(({ ctx, input }) => {
15098
- return createCliStreamObservable((onEvent) => streamOpenSpecCliMutation(ctx, ["init"], (mutationEvent) => ctx.cliExecutor.initStream({
15099
- tools: input?.tools,
15100
- profile: input?.profile,
15101
- force: input?.force
15102
- }, mutationEvent), onEvent));
15103
- }),
15104
15668
  archiveStrictStream: publicProcedure.input(z.object({
15105
15669
  changeId: z.string(),
15670
+ expectedRootGeneration: z.string().min(1),
15106
15671
  skipSpecs: z.boolean().optional(),
15107
15672
  noValidate: z.boolean().optional()
15108
15673
  })).subscription(({ ctx, input }) => {
15109
15674
  return createCliStreamObservable((onEvent) => {
15110
15675
  const changeId = requireCanonicalOpenSpecEntityId(input.changeId, "changeId");
15111
- return startPlanningRootCliStream(ctx, ({ rootContext }, leasedEvent) => {
15676
+ return startPlanningRootCliStream(ctx, ({ rootContext, gitBindingToken }, leasedEvent) => {
15677
+ if (input.expectedRootGeneration !== gitBindingToken) throw new TRPCError({
15678
+ code: "CONFLICT",
15679
+ message: "Planning root changed before archive started. Refresh Archive inputs and try again."
15680
+ });
15112
15681
  const selector = getRootContextCliSelector(rootContext);
15113
15682
  return startStrictArchiveStream({
15114
15683
  skipValidation: input.noValidate === true,
@@ -15129,6 +15698,49 @@ const cliRouter = router({
15129
15698
  });
15130
15699
  })
15131
15700
  });
15701
+ /** Config-owned Agent registry, policy, physical inventory, and CLI mutation boundary. */
15702
+ 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.");
15703
+ const agentIntegrationsRouter = router({
15704
+ get: publicProcedure.query(({ ctx }) => ctx.agentDeliveryProjectionService.getCurrent()),
15705
+ refresh: publicProcedure.query(({ ctx }) => ctx.agentDeliveryProjectionService.refresh()),
15706
+ subscribe: publicProcedure.subscription(({ ctx }) => {
15707
+ return observable((emit) => {
15708
+ const subscription = ctx.agentDeliveryProjectionService.subscribe((event) => {
15709
+ if (event.type === "snapshot") emit.next(event.projection);
15710
+ else emit.error(event.error);
15711
+ });
15712
+ return () => subscription.unsubscribe();
15713
+ });
15714
+ }),
15715
+ updatePolicy: publicProcedure.input(AgentDeliveryPolicyUpdateSchema).mutation(async ({ ctx, input }) => {
15716
+ const config = applyAgentDeliveryPolicy((await ctx.environmentGlobalProjectionService.getCurrent()).config, input);
15717
+ await runOpenSpecCliMutation(ctx, ["config", "set"], () => writeEnvironmentGlobalConfig({
15718
+ cliExecutor: ctx.cliExecutor,
15719
+ config
15720
+ }));
15721
+ ctx.environmentGlobalProjectionService.refreshFile();
15722
+ return ctx.agentDeliveryProjectionService.refresh();
15723
+ }),
15724
+ initStream: publicProcedure.input(z.object({
15725
+ tools: z.union([z.array(agentIntegrationToolIdSchema).min(1), z.literal("all")]),
15726
+ force: z.boolean().optional()
15727
+ })).subscription(({ ctx, input }) => {
15728
+ return createCliStreamObservable(async (onEvent) => {
15729
+ const projection = await ctx.agentDeliveryProjectionService.getCurrent();
15730
+ return streamOpenSpecCliMutation(ctx, ["init"], (mutationEvent) => ctx.cliExecutor.initStream({
15731
+ tools: input.tools,
15732
+ profile: projection.policy.profile,
15733
+ force: input.force
15734
+ }, mutationEvent), onEvent);
15735
+ });
15736
+ }),
15737
+ updateStream: publicProcedure.subscription(({ ctx }) => {
15738
+ return createCliStreamObservable((onEvent) => streamOpenSpecCliMutation(ctx, ["update"], (mutationEvent) => ctx.cliExecutor.executeStream(["update"], mutationEvent), onEvent));
15739
+ }),
15740
+ repairStream: publicProcedure.subscription(({ ctx }) => {
15741
+ return createCliStreamObservable((onEvent) => streamOpenSpecCliMutation(ctx, ["update", "--force"], (mutationEvent) => ctx.cliExecutor.executeStream(["update", "--force"], mutationEvent), onEvent));
15742
+ })
15743
+ });
15132
15744
  /**
15133
15745
  * OPSX router - CLI-driven workflow data
15134
15746
  */
@@ -15187,6 +15799,18 @@ const opsxRouter = router({
15187
15799
  if (data.kind !== "opsx-apply-instructions") throw new Error("Unexpected OPSX Apply-instructions projection.");
15188
15800
  return data.value;
15189
15801
  }),
15802
+ archiveInstructions: publicProcedure.input(z.object({
15803
+ change: z.string().optional(),
15804
+ schema: z.string().optional()
15805
+ })).query(async ({ ctx, input }) => {
15806
+ const data = await runPlanningRootRead(ctx, ({ planningCliProjectionService }) => planningCliProjectionService.getCurrent({
15807
+ kind: "opsx-archive-instructions",
15808
+ change: requireChangeId(input.change),
15809
+ schema: input.schema
15810
+ }));
15811
+ if (data.kind !== "opsx-archive-instructions") throw new Error("Unexpected OPSX Archive-instructions projection.");
15812
+ return data.value;
15813
+ }),
15190
15814
  configBundle: publicProcedure.query(async ({ ctx }) => {
15191
15815
  const data = await runPlanningRootRead(ctx, ({ planningCliProjectionService }) => planningCliProjectionService.getCurrent({ kind: "opsx-config-bundle" }));
15192
15816
  if (data.kind !== "opsx-config-bundle") throw new Error("Unexpected OPSX Config-bundle projection.");
@@ -15886,17 +16510,29 @@ const planningConfigRouter = router({
15886
16510
  subscribeProjectBinding: publicProcedure.subscription(({ ctx }) => createReactiveSubscription(() => fetchProjectBindingConfig(ctx, true))),
15887
16511
  updateProjectBinding: publicProcedure.input(ProjectBindingUpdateSchema).mutation(({ ctx, input }) => updateProjectBindingConfig(ctx, input)),
15888
16512
  activeRoot: publicProcedure.query(({ ctx }) => fetchActiveRootConfig(ctx)),
15889
- subscribeActiveRoot: publicProcedure.subscription(({ ctx }) => createPlanningRootSubscription(ctx, ({ rootContext }) => readActiveRootConfig({
16513
+ subscribeActiveRoot: publicProcedure.subscription(({ ctx }) => createPlanningRootProjectionSubscription(ctx, ({ rootContext }) => readActiveRootConfig({
15890
16514
  launchProjectDir: ctx.projectDir,
15891
16515
  rootContext
15892
16516
  }))),
15893
- writeActiveRoot: publicProcedure.input(z.object({ content: z.string() })).mutation(async ({ ctx, input }) => {
15894
- await runPlanningRoot(ctx, ({ rootContext }) => writeActiveRootConfig({
15895
- launchProjectDir: ctx.projectDir,
15896
- rootContext,
15897
- content: input.content
15898
- }));
15899
- return { success: true };
16517
+ writeActiveRoot: publicProcedure.input(ActiveRootMutationSchema).mutation(async ({ ctx, input }) => {
16518
+ const result = await runPlanningRoot(ctx, async ({ rootContext, planningCliProjectionService }) => {
16519
+ const mutationResult = await mutateActiveRootConfig({
16520
+ launchProjectDir: ctx.projectDir,
16521
+ rootContext,
16522
+ mutation: input
16523
+ });
16524
+ if (mutationResult.state === "applied") planningCliProjectionService.invalidateConfigDependentWork();
16525
+ return mutationResult;
16526
+ });
16527
+ if (result.state === "applied") {
16528
+ ctx.runtimeInvalidation.invalidate([
16529
+ "project",
16530
+ "context",
16531
+ "schemas"
16532
+ ]);
16533
+ ctx.rootContextProjectionService.refresh();
16534
+ }
16535
+ return result;
15900
16536
  }),
15901
16537
  environmentGlobal: publicProcedure.query(({ ctx }) => ctx.environmentGlobalProjectionService.getCurrentConfig()),
15902
16538
  readEnvironmentGlobalProjection: publicProcedure.query(({ ctx }) => EnvironmentGlobalProjectionStateSchema.parse(ctx.environmentGlobalProjectionService.read())),
@@ -15905,14 +16541,6 @@ const planningConfigRouter = router({
15905
16541
  const subscription = ctx.environmentGlobalProjectionService.subscribe((notice) => emit.next(CliProjectionNoticeSchema.parse(notice)));
15906
16542
  return () => subscription.unsubscribe();
15907
16543
  })),
15908
- applyCoreProfile: publicProcedure.mutation(async ({ ctx }) => {
15909
- const args = [
15910
- "config",
15911
- "profile",
15912
- "core"
15913
- ];
15914
- return runOpenSpecCliMutation(ctx, args, () => ctx.cliExecutor.execute(args));
15915
- }),
15916
16544
  readEnvironmentGlobalFileProjection: publicProcedure.query(({ ctx }) => EnvironmentGlobalFileProjectionStateSchema.parse(ctx.environmentGlobalProjectionService.readFile())),
15917
16545
  refreshEnvironmentGlobalFileProjection: publicProcedure.query(({ ctx }) => EnvironmentGlobalFileProjectionStateSchema.parse(ctx.environmentGlobalProjectionService.refreshFile())),
15918
16546
  subscribeEnvironmentGlobalFileProjection: publicProcedure.subscription(({ ctx }) => observable((emit) => {
@@ -15924,7 +16552,32 @@ const planningConfigRouter = router({
15924
16552
  cliExecutor: ctx.cliExecutor,
15925
16553
  config: input.config
15926
16554
  }));
15927
- return { success: true };
16555
+ return {
16556
+ success: true,
16557
+ environment: EnvironmentGlobalProjectionStateSchema.parse(ctx.environmentGlobalProjectionService.refresh()),
16558
+ file: EnvironmentGlobalFileProjectionStateSchema.parse(ctx.environmentGlobalProjectionService.refreshFile()),
16559
+ rootContext: HostedRootContextProjectionStateSchema.parse(ctx.rootContextProjectionService.refresh())
16560
+ };
16561
+ }),
16562
+ writeEnvironmentDefaultStore: publicProcedure.input(EnvironmentDefaultStoreUpdateSchema).mutation(async ({ ctx, input }) => {
16563
+ const evidence = await writeEnvironmentDefaultStore({
16564
+ cliExecutor: ctx.cliExecutor,
16565
+ update: input
16566
+ });
16567
+ return {
16568
+ kind: "environment-default-store-update",
16569
+ configured: input.value ? {
16570
+ state: "configured",
16571
+ id: input.value
16572
+ } : {
16573
+ state: "absent",
16574
+ id: null
16575
+ },
16576
+ evidence,
16577
+ environment: EnvironmentGlobalProjectionStateSchema.parse(ctx.environmentGlobalProjectionService.refresh()),
16578
+ file: EnvironmentGlobalFileProjectionStateSchema.parse(ctx.environmentGlobalProjectionService.refreshFile()),
16579
+ rootContext: HostedRootContextProjectionStateSchema.parse(ctx.rootContextProjectionService.refresh())
16580
+ };
15928
16581
  })
15929
16582
  });
15930
16583
  /** CLI-owned planning Root Context shared by every project-workspace surface. */
@@ -15941,6 +16594,7 @@ const rootContextRouter = router({
15941
16594
  * Main app router
15942
16595
  */
15943
16596
  const appRouter = router({
16597
+ agentIntegrations: agentIntegrationsRouter,
15944
16598
  dashboard: dashboardRouter,
15945
16599
  git: gitRouter,
15946
16600
  spec: specRouter,
@@ -16803,44 +17457,6 @@ var StoreProjectionService = class {
16803
17457
  }
16804
17458
  };
16805
17459
 
16806
- //#endregion
16807
- //#region src/tool-command-observation-service.ts
16808
- /** Server-owned lifecycle for non-project tool command artifact observation. */
16809
- var ToolCommandObservationService = class {
16810
- startPromise = null;
16811
- releaseRoot = null;
16812
- disposed = false;
16813
- constructor(environment, root) {
16814
- this.environment = environment;
16815
- this.root = root;
16816
- }
16817
- /** Acquire the Codex command directory, using missing-root watcher ownership when necessary. */
16818
- start() {
16819
- if (this.disposed) return Promise.reject(/* @__PURE__ */ new Error("Cannot start tool command observation after teardown."));
16820
- if (this.startPromise) return this.startPromise;
16821
- const startPromise = this.environment.acquireRoot(this.root).then(async (releaseRoot) => {
16822
- if (this.disposed) {
16823
- await releaseRoot();
16824
- return;
16825
- }
16826
- this.releaseRoot = releaseRoot;
16827
- }).finally(() => {
16828
- if (this.startPromise === startPromise && this.releaseRoot === null) this.startPromise = null;
16829
- });
16830
- this.startPromise = startPromise;
16831
- return startPromise;
16832
- }
16833
- /** Release external command observation, including a startup that is still settling. */
16834
- async dispose() {
16835
- if (this.disposed) return;
16836
- this.disposed = true;
16837
- await this.startPromise?.catch(() => {});
16838
- const releaseRoot = this.releaseRoot;
16839
- this.releaseRoot = null;
16840
- await releaseRoot?.();
16841
- }
16842
- };
16843
-
16844
17460
  //#endregion
16845
17461
  //#region src/tracing.ts
16846
17462
  /**
@@ -18252,7 +18868,7 @@ function getManagedLocalRuntimeSpec(engineId) {
18252
18868
  return {
18253
18869
  packageNames: ["ctranslate2"],
18254
18870
  allowBuildPackages: ["ctranslate2"],
18255
- fallbackRange: "^0.1.0",
18871
+ fallbackRange: "^1.0.0",
18256
18872
  detectMissing(tree) {
18257
18873
  return hasRuntimePackageDependencyPath(tree, ["ctranslate2"]) ? [] : ["ctranslate2"];
18258
18874
  }
@@ -18794,18 +19410,12 @@ if (!isMainThread) {
18794
19410
  //#endregion
18795
19411
  //#region src/server.ts
18796
19412
  /**
18797
- * Orthogonal intents (updated 2026-07-31 Asia/Shanghai):
18798
- * 1. Bootstrap the HTTP/tRPC server and launch-project runtime services.
18799
- * 2. Delegate OpenSpec filesystem ownership to the CLI-selected planning-root manager.
18800
- * 3. Host Server-local terminal/Root Context notifications, sound, preview-resource, and translation HTTP boundaries.
18801
- * 4. Host tRPC and PTY WebSocket transports with deterministic teardown.
18802
- * PTY planning-root creation and workflow input verify opaque Root generation provenance.
18803
- * 5. Own the runtime observation environment and external Codex command lease while deferring expensive
18804
- * root-scoped projections until a foreground consumer requests them; keep Store-list registry/mutation
18805
- * evidence distinct from selector-exact Store-root observation.
18806
- * 6. Initialize backend OpenTelemetry tracing (--otel diagnostic only) and expose the resolved Tracer
18807
- * through Context so procedure/planning-root instrumentation stays branch-free.
18808
- * 7. Admit dynamic loopback App origins while keeping remote browser origins explicitly allowlisted.
19413
+ * Orthogonal intents (updated 2026-08-01 Asia/Shanghai):
19414
+ * 1. Bootstrap HTTP/tRPC, WebSocket, PTY, and launch-project runtime services.
19415
+ * 2. Delegate planning-root, Store, Environment Global, Agent, and Root Context projection ownership.
19416
+ * 3. Host notifications, sound, preview-resource, translation, and telemetry boundaries.
19417
+ * 4. Admit dynamic loopback App origins and perform deterministic runtime teardown.
19418
+ * 5. Bridge settled machine-global config changes into Root Context replacement.
18809
19419
  *
18810
19420
  * Original request (2026-07-15): "你先负责后端(内核)的开发。"
18811
19421
  * Original request (2026-07-17): "Every Planning-root execution surface uses the same operation lifetime owner."
@@ -18821,6 +19431,8 @@ if (!isMainThread) {
18821
19431
  * Original request (2026-07-26): "展开全面的接口升级和内核升级和测试升级。"
18822
19432
  * Built-runtime defect (2026-07-30): Direct Web shutdown must await HTTP closure and retire non-cooperative WebSocket and buffered CLI children after one signal.
18823
19433
  * Original request (2026-07-31): Trace every Planning-root write lock with timing, source, and stack evidence.
19434
+ * Original request (2026-08-01): adapt OpenSpec 1.7 machine `defaultStore` fallback.
19435
+ * Original request (2026-08-01): move Agent delivery projection ownership into the Server runtime.
18824
19436
  *
18825
19437
  * @module server
18826
19438
  */
@@ -18887,19 +19499,12 @@ function createServer(config) {
18887
19499
  storeObservation,
18888
19500
  observationEnvironment
18889
19501
  });
18890
- const toolCommandObservation = new ToolCommandObservationService(observationEnvironment, getExternalCodexCommandObservationRoot());
18891
19502
  const codeGitBinding = new LaunchGitRepositoryBindingOwner();
18892
19503
  const projectionWorkRuntime = createServerProjectionWorkRuntime();
18893
19504
  const dashboardProjectionWorkOwner = createDashboardProjectionWorkOwner(projectionWorkRuntime);
18894
19505
  const changesProjectionWorkOwner = createChangesProjectionWorkOwner(projectionWorkRuntime);
18895
19506
  const storeProjectionWorkOwner = createStoreProjectionWorkOwner(projectionWorkRuntime);
18896
19507
  const planningCliProjectionWorkOwner = createPlanningCliProjectionWorkOwner(projectionWorkRuntime);
18897
- const environmentGlobalProjectionService = new EnvironmentGlobalProjectionService({
18898
- dataScope,
18899
- cliExecutor,
18900
- observationEnvironment,
18901
- workOwner: createEnvironmentGlobalProjectionWorkOwner(projectionWorkRuntime)
18902
- });
18903
19508
  const storeProjectionService = new StoreProjectionService({
18904
19509
  dataScopePath: dataScope.path,
18905
19510
  cliExecutor,
@@ -18935,6 +19540,21 @@ function createServer(config) {
18935
19540
  planningRootServices,
18936
19541
  workOwner: createRootContextProjectionWorkOwner(projectionWorkRuntime)
18937
19542
  });
19543
+ const environmentGlobalProjectionService = new EnvironmentGlobalProjectionService({
19544
+ dataScope,
19545
+ cliExecutor,
19546
+ observationEnvironment,
19547
+ workOwner: createEnvironmentGlobalProjectionWorkOwner(projectionWorkRuntime),
19548
+ onConfigFileSettled: () => rootContextProjectionService.refresh()
19549
+ });
19550
+ environmentGlobalProjectionService.start();
19551
+ const agentDeliveryProjectionService = new AgentDeliveryProjectionService({
19552
+ projectDir: config.projectDir,
19553
+ environmentGlobalProjectionService,
19554
+ observationEnvironment,
19555
+ cliExecutor,
19556
+ cliCommandAuthority: configManager
19557
+ });
18938
19558
  const gitRepositoryBindings = new GitRepositoryBindingService({
18939
19559
  launchProjectDir: config.projectDir,
18940
19560
  planningRootServices,
@@ -19128,8 +19748,8 @@ function createServer(config) {
19128
19748
  storeContentProjectionService,
19129
19749
  rootContextProjectionService,
19130
19750
  environmentGlobalProjectionService,
19751
+ agentDeliveryProjectionService,
19131
19752
  storeMutationService,
19132
- toolCommandObservation,
19133
19753
  configManager,
19134
19754
  cliExecutor,
19135
19755
  projectRecoveryService,
@@ -19170,8 +19790,8 @@ function createServer(config) {
19170
19790
  storeContentProjectionService,
19171
19791
  rootContextProjectionService,
19172
19792
  environmentGlobalProjectionService,
19793
+ agentDeliveryProjectionService,
19173
19794
  storeMutationService,
19174
- toolCommandObservation,
19175
19795
  configManager,
19176
19796
  cliExecutor,
19177
19797
  projectRecoveryService,
@@ -19206,8 +19826,8 @@ function createServer(config) {
19206
19826
  storeContentProjectionService,
19207
19827
  rootContextProjectionService,
19208
19828
  environmentGlobalProjectionService,
19829
+ agentDeliveryProjectionService,
19209
19830
  storeObservationFallback,
19210
- toolCommandObservation,
19211
19831
  adapter,
19212
19832
  configManager,
19213
19833
  cliExecutor,
@@ -19323,13 +19943,13 @@ async function createWebSocketServer(server, httpServer, config) {
19323
19943
  await settleCleanupPhase(failures, [() => server.rootContextNotificationBridge.dispose()]);
19324
19944
  await settleCleanupPhase(failures, [() => server.storeProjectionService.dispose()]);
19325
19945
  await settleCleanupPhase(failures, [() => server.storeContentProjectionService.dispose()]);
19946
+ await settleCleanupPhase(failures, [() => server.agentDeliveryProjectionService.dispose()]);
19326
19947
  await settleCleanupPhase(failures, [() => server.environmentGlobalProjectionService.dispose()]);
19327
19948
  await settleCleanupPhase(failures, [() => server.cliExecutor.dispose()]);
19328
19949
  await settleCleanupPhase(failures, [() => server.planningRootServices.dispose()]);
19329
19950
  await settleCleanupPhase(failures, [() => server.projectionWorkRuntime.clear()]);
19330
19951
  await settleCleanupPhase(failures, [() => server.storeObservation.dispose()]);
19331
19952
  await settleCleanupPhase(failures, [() => server.dataHomeObserver.dispose()]);
19332
- await settleCleanupPhase(failures, [() => server.toolCommandObservation.dispose()]);
19333
19953
  await settleCleanupPhase(failures, [() => server.projectInvalidation.dispose()]);
19334
19954
  await settleCleanupPhase(failures, [() => server.observationEnvironment.dispose()]);
19335
19955
  await settleCleanupPhase(failures, [() => server.projectRecoveryService.dispose(), () => server.translationCacheService.close()]);