@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.
- package/dist/index.mjs +842 -222
- 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-
|
|
5761
|
-
* 1. Read launch-project binding and
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
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
|
|
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 (
|
|
5947
|
-
* 1. Own Environment Global CLI truth
|
|
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
|
|
5951
|
-
* 5.
|
|
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.
|
|
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.
|
|
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-
|
|
8302
|
-
* 1. Own selector-exact OPSX and Spec Projection Work
|
|
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:
|
|
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:
|
|
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-
|
|
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
|
|
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
|
|
10629
|
+
"CLI-owned archive-instructions evidence:",
|
|
10317
10630
|
"```json",
|
|
10318
|
-
normalized.length > 0 ? normalized : "(no
|
|
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.
|
|
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 =
|
|
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({
|
|
14760
|
-
|
|
14761
|
-
|
|
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()
|
|
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()
|
|
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(
|
|
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.
|
|
14995
|
-
|
|
14996
|
-
|
|
14997
|
-
|
|
14998
|
-
|
|
14999
|
-
|
|
15000
|
-
|
|
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 }) =>
|
|
16513
|
+
subscribeActiveRoot: publicProcedure.subscription(({ ctx }) => createPlanningRootProjectionSubscription(ctx, ({ rootContext }) => readActiveRootConfig({
|
|
15890
16514
|
launchProjectDir: ctx.projectDir,
|
|
15891
16515
|
rootContext
|
|
15892
16516
|
}))),
|
|
15893
|
-
writeActiveRoot: publicProcedure.input(
|
|
15894
|
-
await runPlanningRoot(ctx, ({ rootContext }) =>
|
|
15895
|
-
|
|
15896
|
-
|
|
15897
|
-
|
|
15898
|
-
|
|
15899
|
-
|
|
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 {
|
|
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: "^
|
|
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-
|
|
18798
|
-
* 1. Bootstrap
|
|
18799
|
-
* 2. Delegate
|
|
18800
|
-
* 3. Host
|
|
18801
|
-
* 4.
|
|
18802
|
-
*
|
|
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()]);
|