@nextclaw/kernel 0.15.0-beta.0 → 0.15.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.d.ts +227 -48
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1385 -268
- package/dist/index.js.map +1 -1
- package/package.json +12 -12
package/dist/index.js
CHANGED
|
@@ -11,7 +11,7 @@ import { DefaultNcpAgentConversationStateManager, insertMessageByTimeline, merge
|
|
|
11
11
|
import { accessSync, appendFileSync, chmodSync, constants, createReadStream, existsSync, mkdirSync, readFileSync, readdirSync, readlinkSync, realpathSync, renameSync, rmSync, writeFileSync } from "node:fs";
|
|
12
12
|
import path, { basename, delimiter, dirname, extname, isAbsolute, join, normalize, relative, resolve, sep } from "node:path";
|
|
13
13
|
import { access, appendFile, chmod, lstat, mkdir, mkdtemp, open, readFile, readdir, realpath, rename, rm, stat, unlink, writeFile } from "node:fs/promises";
|
|
14
|
-
import { AppHomeService, AppInstallationService, AppInstanceInventoryService, AppInstanceStorageService, AppManifestService, AppPlatformTargetService, AppRegistryService, AppServiceLaunchService, FileLockService, isAppComponentManifestBundle } from "@nextclaw/app-runtime";
|
|
14
|
+
import { AppGrantService, AppHomeService, AppInstallationService, AppInstanceInventoryService, AppInstanceStorageService, AppManifestService, AppPlatformTargetService, AppRegistryService, AppServiceLaunchService, FileLockService, isAppComponentManifestBundle } from "@nextclaw/app-runtime";
|
|
15
15
|
import { execFileSync, spawn } from "node:child_process";
|
|
16
16
|
import { fileURLToPath } from "node:url";
|
|
17
17
|
import { BUILTIN_PROVIDER_PLUGINS } from "@nextclaw/runtime";
|
|
@@ -3664,7 +3664,11 @@ var AppPackageReadinessManager = class {
|
|
|
3664
3664
|
isolation: manifestBundle.manifest.main.kind === "wasi-http-component" ? "host-mediated" : "sandboxed",
|
|
3665
3665
|
permissions: manifestBundle.manifest.permissions ?? {}
|
|
3666
3666
|
};
|
|
3667
|
-
const [dependencies, secrets] = await Promise.all([
|
|
3667
|
+
const [dependencies, secrets, documentAccess] = await Promise.all([
|
|
3668
|
+
this.params.dependencyCoordinator.inspectTarget(this.params.dependencyCoordinator.targetForInfo(info, selectedVersion), providers),
|
|
3669
|
+
this.inspectSecretsForInfo(info, true, selectedVersion),
|
|
3670
|
+
this.params.grantService.summarize(info.appId).then((state) => state.documentAccess)
|
|
3671
|
+
]);
|
|
3668
3672
|
return {
|
|
3669
3673
|
id: info.appId,
|
|
3670
3674
|
name: info.name,
|
|
@@ -3701,7 +3705,8 @@ var AppPackageReadinessManager = class {
|
|
|
3701
3705
|
isolation: security.isolation,
|
|
3702
3706
|
readiness: this.combineReadiness(dependencies.readiness, secrets.readiness),
|
|
3703
3707
|
secrets,
|
|
3704
|
-
dependencies
|
|
3708
|
+
dependencies,
|
|
3709
|
+
documentAccess
|
|
3705
3710
|
};
|
|
3706
3711
|
};
|
|
3707
3712
|
inspectSecrets = async (info, verify, selectedVersion = info.activeVersion) => {
|
|
@@ -4537,6 +4542,73 @@ var AppPackageDependencyCoordinator = class {
|
|
|
4537
4542
|
};
|
|
4538
4543
|
};
|
|
4539
4544
|
//#endregion
|
|
4545
|
+
//#region src/services/app-package-document-access.service.ts
|
|
4546
|
+
var AppPackageDocumentAccessService = class {
|
|
4547
|
+
constructor(params) {
|
|
4548
|
+
this.params = params;
|
|
4549
|
+
}
|
|
4550
|
+
inspect = async (appId) => await this.params.installationService.withAppOperation(appId, async () => {
|
|
4551
|
+
try {
|
|
4552
|
+
return await this.params.grantService.summarize(appId);
|
|
4553
|
+
} catch (error) {
|
|
4554
|
+
throw this.toError(error);
|
|
4555
|
+
}
|
|
4556
|
+
});
|
|
4557
|
+
assert = async (appId, scopeId, requestedMode) => {
|
|
4558
|
+
const scope = (await this.inspect(appId)).documentAccess.find((entry) => entry.id === scopeId);
|
|
4559
|
+
if (!scope) throw new AppPackageError("DOCUMENT_SCOPE_NOT_DECLARED", `App ${appId} does not declare document scope ${scopeId}.`);
|
|
4560
|
+
if (!scope.granted) throw new AppPackageError("DOCUMENT_SCOPE_NOT_GRANTED", `App ${appId} requires document scope ${scopeId}. Grant it and retry the action.`);
|
|
4561
|
+
if (scope.status === "unavailable") throw new AppPackageError("DOCUMENT_SCOPE_UNAVAILABLE", `App ${appId} document scope ${scopeId} is unavailable. Replace or revoke it.`);
|
|
4562
|
+
if (requestedMode === "read-write" && scope.effectiveMode !== "read-write") throw new AppPackageError("DOCUMENT_SCOPE_MODE_INSUFFICIENT", `App ${appId} document scope ${scopeId} needs read-write access.`);
|
|
4563
|
+
};
|
|
4564
|
+
grant = async (appId, input) => await this.mutate(appId, input.scopeId, async () => await this.params.grantService.grantDocumentScope({
|
|
4565
|
+
appId,
|
|
4566
|
+
...input
|
|
4567
|
+
}));
|
|
4568
|
+
revoke = async (appId, scopeId) => await this.mutate(appId, scopeId, async () => {
|
|
4569
|
+
if (!(await this.params.grantService.summarize(appId)).documentAccess.some((scope) => scope.id === scopeId)) throw new AppPackageError("DOCUMENT_SCOPE_NOT_DECLARED", `App ${appId} does not declare document scope ${scopeId}.`);
|
|
4570
|
+
return await this.params.grantService.revokeDocumentScope({
|
|
4571
|
+
appId,
|
|
4572
|
+
scopeId
|
|
4573
|
+
});
|
|
4574
|
+
});
|
|
4575
|
+
mutate = async (appId, scopeId, mutation) => await this.params.installationService.withAppOperation(appId, async () => {
|
|
4576
|
+
const app = await this.params.getPackage(appId);
|
|
4577
|
+
const previousGrant = (await this.params.registryService.getApp(appId))?.grants[scopeId];
|
|
4578
|
+
const sources = this.toComponentSources(app);
|
|
4579
|
+
const runtimeHooks = this.params.getRuntimeHooks();
|
|
4580
|
+
const rollbackRuntime = app.enabled ? await runtimeHooks.prepareCapabilityChange(sources) : async () => void 0;
|
|
4581
|
+
try {
|
|
4582
|
+
const result = await mutation();
|
|
4583
|
+
if (app.enabled) await runtimeHooks.afterCapabilityChange(sources);
|
|
4584
|
+
return result;
|
|
4585
|
+
} catch (error) {
|
|
4586
|
+
const recoveryErrors = [];
|
|
4587
|
+
try {
|
|
4588
|
+
await this.params.registryService.restoreDocumentGrant(appId, scopeId, previousGrant);
|
|
4589
|
+
} catch (recoveryError) {
|
|
4590
|
+
recoveryErrors.push(recoveryError);
|
|
4591
|
+
}
|
|
4592
|
+
try {
|
|
4593
|
+
await rollbackRuntime();
|
|
4594
|
+
} catch (recoveryError) {
|
|
4595
|
+
recoveryErrors.push(recoveryError);
|
|
4596
|
+
}
|
|
4597
|
+
if (recoveryErrors.length > 0) throw new AggregateError([error, ...recoveryErrors], `App ${appId} document scope ${scopeId} mutation failed and recovery was incomplete.`);
|
|
4598
|
+
throw this.toError(error);
|
|
4599
|
+
}
|
|
4600
|
+
});
|
|
4601
|
+
toComponentSources = (app) => app.components.map((component) => ({ ...component }));
|
|
4602
|
+
toError = (error) => {
|
|
4603
|
+
if (error instanceof AppPackageError) return error;
|
|
4604
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
4605
|
+
if (message.includes("未声明 documentAccess scope")) return new AppPackageError("DOCUMENT_SCOPE_NOT_DECLARED", message);
|
|
4606
|
+
if (message.includes("只声明了 read")) return new AppPackageError("DOCUMENT_SCOPE_MODE_INSUFFICIENT", message);
|
|
4607
|
+
if (message.includes("不是可用目录")) return new AppPackageError("DOCUMENT_SCOPE_UNAVAILABLE", message);
|
|
4608
|
+
return new AppPackageError("DOCUMENT_SCOPE_MUTATION_FAILED", message);
|
|
4609
|
+
};
|
|
4610
|
+
};
|
|
4611
|
+
//#endregion
|
|
4540
4612
|
//#region src/services/app-package-host-target.service.ts
|
|
4541
4613
|
var AppPackageHostTargetService = class {
|
|
4542
4614
|
platformTargetService = new AppPlatformTargetService();
|
|
@@ -4607,6 +4679,8 @@ const EMPTY_APP_PACKAGE_RUNTIME_HOOKS = {
|
|
|
4607
4679
|
assertCanActivate: async () => void 0,
|
|
4608
4680
|
afterActivate: async () => void 0,
|
|
4609
4681
|
beforeDeactivate: async () => void 0,
|
|
4682
|
+
prepareCapabilityChange: async () => async () => void 0,
|
|
4683
|
+
afterCapabilityChange: async () => void 0,
|
|
4610
4684
|
beforeUninstall: async () => void 0
|
|
4611
4685
|
};
|
|
4612
4686
|
var AppPackageRuntimeActivationService = class {
|
|
@@ -4637,6 +4711,8 @@ var AppPackageManager = class {
|
|
|
4637
4711
|
dependencyCoordinator;
|
|
4638
4712
|
runtimeActivationService = new AppPackageRuntimeActivationService();
|
|
4639
4713
|
registryService;
|
|
4714
|
+
grantService;
|
|
4715
|
+
documentAccessService;
|
|
4640
4716
|
readinessManager;
|
|
4641
4717
|
runtimeHooks = EMPTY_APP_PACKAGE_RUNTIME_HOOKS;
|
|
4642
4718
|
builtInBootstrapPromise;
|
|
@@ -4646,6 +4722,14 @@ var AppPackageManager = class {
|
|
|
4646
4722
|
this.appHomeService = new AppHomeService(params.appHomeDirectory);
|
|
4647
4723
|
this.installationService = new AppInstallationService(this.appHomeService);
|
|
4648
4724
|
this.registryService = new AppRegistryService(this.appHomeService);
|
|
4725
|
+
this.grantService = new AppGrantService(this.registryService, this.manifestService);
|
|
4726
|
+
this.documentAccessService = new AppPackageDocumentAccessService({
|
|
4727
|
+
grantService: this.grantService,
|
|
4728
|
+
installationService: this.installationService,
|
|
4729
|
+
registryService: this.registryService,
|
|
4730
|
+
getPackage: this.getPackage,
|
|
4731
|
+
getRuntimeHooks: () => this.runtimeHooks
|
|
4732
|
+
});
|
|
4649
4733
|
this.dependencyCoordinator = new AppPackageDependencyCoordinator({
|
|
4650
4734
|
installationService: this.installationService,
|
|
4651
4735
|
registryService: this.registryService,
|
|
@@ -4658,6 +4742,7 @@ var AppPackageManager = class {
|
|
|
4658
4742
|
presentationService: this.presentationService,
|
|
4659
4743
|
dependencyCoordinator: this.dependencyCoordinator,
|
|
4660
4744
|
registryService: this.registryService,
|
|
4745
|
+
grantService: this.grantService,
|
|
4661
4746
|
productVersion: params.productVersion,
|
|
4662
4747
|
getSecretConfig: params.getSecretConfig,
|
|
4663
4748
|
secretConfigPath: params.secretConfigPath,
|
|
@@ -4718,6 +4803,12 @@ var AppPackageManager = class {
|
|
|
4718
4803
|
await this.registryService.unbindSecret(appId, slotId);
|
|
4719
4804
|
return await this.inspectSecrets(appId);
|
|
4720
4805
|
});
|
|
4806
|
+
inspectDocumentAccess = async (appId) => await this.documentAccessService.inspect(appId);
|
|
4807
|
+
assertDocumentAccess = async (appId, scopeId, requestedMode) => {
|
|
4808
|
+
await this.documentAccessService.assert(appId, scopeId, requestedMode);
|
|
4809
|
+
};
|
|
4810
|
+
grantDocumentAccess = async (appId, input) => await this.documentAccessService.grant(appId, input);
|
|
4811
|
+
revokeDocumentAccess = async (appId, scopeId) => await this.documentAccessService.revoke(appId, scopeId);
|
|
4721
4812
|
listActiveComponentSources = async () => (await this.listActiveComponentSourcesWithDiagnostics()).sources;
|
|
4722
4813
|
listActiveComponentSourcesWithDiagnostics = async () => await this.dependencyCoordinator.listActiveComponentSourcesWithDiagnostics();
|
|
4723
4814
|
listOperations = async () => await this.operationManager.list();
|
|
@@ -6590,6 +6681,9 @@ function isRecord$18(value) {
|
|
|
6590
6681
|
//#region src/utils/service-app-error.utils.ts
|
|
6591
6682
|
const SERVICE_APP_ERROR_CODES = new Set([
|
|
6592
6683
|
"AUTHORIZATION_REQUIRED",
|
|
6684
|
+
"DOCUMENT_SCOPE_NOT_GRANTED",
|
|
6685
|
+
"DOCUMENT_SCOPE_MODE_INSUFFICIENT",
|
|
6686
|
+
"DOCUMENT_SCOPE_UNAVAILABLE",
|
|
6593
6687
|
"SERVICE_APP_ACTION_NOT_DECLARED",
|
|
6594
6688
|
"SERVICE_APP_ACTION_NOT_FOUND",
|
|
6595
6689
|
"SERVICE_APP_INVALID_ACTION",
|
|
@@ -7650,23 +7744,23 @@ var DesktopNodeReplService = class {
|
|
|
7650
7744
|
});
|
|
7651
7745
|
case "setValue": return await this.sessionState.setValue(caller, {
|
|
7652
7746
|
target,
|
|
7653
|
-
stateId: requiredString$
|
|
7747
|
+
stateId: requiredString$2(args.stateId, "stateId"),
|
|
7654
7748
|
elementIndex: readElementIndex(args.element),
|
|
7655
7749
|
value: requiredValue$1(args.value ?? args.text, "value")
|
|
7656
7750
|
});
|
|
7657
7751
|
case "click": return await this.sessionState.click(caller, {
|
|
7658
7752
|
target,
|
|
7659
|
-
stateId: requiredString$
|
|
7753
|
+
stateId: requiredString$2(args.stateId, "stateId"),
|
|
7660
7754
|
...args.coordinate === void 0 ? { elementIndex: readElementIndex(args.element) } : { coordinate: readCoordinate(args.coordinate) }
|
|
7661
7755
|
});
|
|
7662
7756
|
case "typeText": return await this.sessionState.typeText(caller, {
|
|
7663
7757
|
target,
|
|
7664
|
-
stateId: requiredString$
|
|
7758
|
+
stateId: requiredString$2(args.stateId, "stateId"),
|
|
7665
7759
|
text: requiredText(args.text, "text")
|
|
7666
7760
|
});
|
|
7667
7761
|
case "pressKey": return await this.sessionState.pressKey(caller, {
|
|
7668
7762
|
target,
|
|
7669
|
-
stateId: requiredString$
|
|
7763
|
+
stateId: requiredString$2(args.stateId, "stateId"),
|
|
7670
7764
|
key: readKeyboardKey(args.key),
|
|
7671
7765
|
...args.modifiers === void 0 ? {} : { modifiers: readKeyboardModifiers(args.modifiers) }
|
|
7672
7766
|
});
|
|
@@ -7713,7 +7807,7 @@ function pickCaller(input) {
|
|
|
7713
7807
|
};
|
|
7714
7808
|
}
|
|
7715
7809
|
function readTarget(value) {
|
|
7716
|
-
return { applicationId: requiredString$
|
|
7810
|
+
return { applicationId: requiredString$2(asRecord$1(value).applicationId, "target.applicationId") };
|
|
7717
7811
|
}
|
|
7718
7812
|
function readElementIndex(value) {
|
|
7719
7813
|
const index = asRecord$1(value).index;
|
|
@@ -7737,7 +7831,7 @@ function isFiniteNumber$1(value) {
|
|
|
7737
7831
|
function asRecord$1(value) {
|
|
7738
7832
|
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
7739
7833
|
}
|
|
7740
|
-
function requiredString$
|
|
7834
|
+
function requiredString$2(value, field) {
|
|
7741
7835
|
if (typeof value !== "string" || !value.trim()) throw replError("invalid_tool_arguments", `${field} must be a non-empty string.`);
|
|
7742
7836
|
return value.trim();
|
|
7743
7837
|
}
|
|
@@ -7769,7 +7863,7 @@ const SUPPORTED_KEY_MODIFIERS = new Set([
|
|
|
7769
7863
|
"shift"
|
|
7770
7864
|
]);
|
|
7771
7865
|
function readKeyboardKey(value) {
|
|
7772
|
-
const key = requiredString$
|
|
7866
|
+
const key = requiredString$2(value, "key");
|
|
7773
7867
|
if (!SUPPORTED_KEYS.has(key)) throw replError("invalid_tool_arguments", "key must be a supported named key or a lowercase letter/digit.");
|
|
7774
7868
|
return key;
|
|
7775
7869
|
}
|
|
@@ -13644,16 +13738,15 @@ function applySessionRuntimePatch(metadata, patch) {
|
|
|
13644
13738
|
function applySessionSettingsMetadataPatch(currentMetadata, patch) {
|
|
13645
13739
|
return applySessionRuntimePatch(applySessionPreferencePatch(structuredClone(currentMetadata), patch), patch);
|
|
13646
13740
|
}
|
|
13647
|
-
async function applySessionProjectMetadataPatch(metadata, patch,
|
|
13741
|
+
async function applySessionProjectMetadataPatch(metadata, patch, normalizeProjectContext) {
|
|
13648
13742
|
if (!Object.prototype.hasOwnProperty.call(patch, "projectRoot")) return metadata;
|
|
13649
|
-
const
|
|
13650
|
-
const { projectRoot: _legacyProjectRoot, ...nextMetadata } = metadata;
|
|
13651
|
-
|
|
13743
|
+
const project = await normalizeProjectContext(patch.projectRoot);
|
|
13744
|
+
const { projectRoot: _legacyProjectRoot, projectId: _legacyProjectId, project_root: _projectRoot, project_id: _projectId, ...nextMetadata } = metadata;
|
|
13745
|
+
return project ? {
|
|
13652
13746
|
...nextMetadata,
|
|
13653
|
-
|
|
13654
|
-
|
|
13655
|
-
|
|
13656
|
-
return metadataWithoutProjectRoot;
|
|
13747
|
+
project_id: project.projectId,
|
|
13748
|
+
project_root: project.rootPath
|
|
13749
|
+
} : nextMetadata;
|
|
13657
13750
|
}
|
|
13658
13751
|
function publishSessionMetadataChanged(eventBus, sessionKey, metadata, mode) {
|
|
13659
13752
|
eventBus.emit(eventKeys$1.sessionMetadataChanged, {
|
|
@@ -13715,6 +13808,9 @@ function readThinkingEffort(metadata) {
|
|
|
13715
13808
|
function readProjectRoot(metadata) {
|
|
13716
13809
|
return readOptionalMetadataString(metadata?.project_root) ?? readOptionalMetadataString(metadata?.projectRoot);
|
|
13717
13810
|
}
|
|
13811
|
+
function readProjectId(metadata) {
|
|
13812
|
+
return readOptionalMetadataString(metadata?.project_id) ?? readOptionalMetadataString(metadata?.projectId);
|
|
13813
|
+
}
|
|
13718
13814
|
function readAgentRuntimeId(metadata) {
|
|
13719
13815
|
return readOptionalMetadataString(metadata?.agentRuntimeId) ?? readOptionalMetadataString(metadata?.runtime) ?? readOptionalMetadataString(metadata?.session_type);
|
|
13720
13816
|
}
|
|
@@ -13731,6 +13827,7 @@ function cloneInheritedMetadata(sourceMetadata) {
|
|
|
13731
13827
|
"preferred_model",
|
|
13732
13828
|
"preferred_thinking",
|
|
13733
13829
|
"project_root",
|
|
13830
|
+
"project_id",
|
|
13734
13831
|
"codex_runtime_backend",
|
|
13735
13832
|
"reasoningNormalizationMode",
|
|
13736
13833
|
"reasoning_normalization_mode"
|
|
@@ -13784,6 +13881,9 @@ function resolveKernelPreferenceStorePath(options) {
|
|
|
13784
13881
|
function resolveKernelProjectStorePath(options) {
|
|
13785
13882
|
return resolveKernelDataPath(options, "projects", "projects.json");
|
|
13786
13883
|
}
|
|
13884
|
+
function resolveKernelProjectWorkStorePath(options) {
|
|
13885
|
+
return resolveKernelDataPath(options, "projects", "work-items.db");
|
|
13886
|
+
}
|
|
13787
13887
|
function resolveKernelInboxDeliveryStorePath(options) {
|
|
13788
13888
|
return resolveKernelDataPath(options, "inbox", "deliveries.json");
|
|
13789
13889
|
}
|
|
@@ -15105,10 +15205,14 @@ var PortableServiceCapabilityResolverService = class {
|
|
|
15105
15205
|
if (!record || app.packageVersion && record.activeVersion !== app.packageVersion) return [];
|
|
15106
15206
|
const resolved = [];
|
|
15107
15207
|
for (const scope of scopes) {
|
|
15108
|
-
const
|
|
15109
|
-
if (
|
|
15110
|
-
|
|
15111
|
-
|
|
15208
|
+
const grant = record.grants[scope.id];
|
|
15209
|
+
if (!grant || grant.mode === "read-write" && scope.mode !== "read-write") continue;
|
|
15210
|
+
resolved.push({
|
|
15211
|
+
scope: {
|
|
15212
|
+
...scope,
|
|
15213
|
+
mode: grant.mode
|
|
15214
|
+
},
|
|
15215
|
+
hostPath: await this.requireCanonicalDirectory(grant.path, `${app.id}:${scope.id}`)
|
|
15112
15216
|
});
|
|
15113
15217
|
}
|
|
15114
15218
|
return resolved;
|
|
@@ -17340,6 +17444,7 @@ function projectCapabilityProviders(entries) {
|
|
|
17340
17444
|
//#endregion
|
|
17341
17445
|
//#region src/managers/service-app.manager.ts
|
|
17342
17446
|
const SERVICE_APP_ID_PATTERN = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
17447
|
+
const DOCUMENT_GUEST_PATH_PATTERN = /^\/documents\/([^/]+)(?:\/|$)/;
|
|
17343
17448
|
var ServiceAppManager = class {
|
|
17344
17449
|
removalService = new ServiceAppRemovalService();
|
|
17345
17450
|
runtimeService;
|
|
@@ -17488,6 +17593,7 @@ var ServiceAppManager = class {
|
|
|
17488
17593
|
const action = listServiceAppManifestActions(record, manifest).find((entry) => entry.id === actionId);
|
|
17489
17594
|
if (!action) throw new ServiceAppError("SERVICE_APP_ACTION_NOT_FOUND", "service action not found");
|
|
17490
17595
|
if (!await this.actionGrants.isGranted(request.caller, action)) throw new ServiceAppError("AUTHORIZATION_REQUIRED", `This panel app needs permission to call ${actionId}.`);
|
|
17596
|
+
await this.assertDocumentInputAccess(record, action.risk, request.input ?? {});
|
|
17491
17597
|
return await this.jobManager.invoke({
|
|
17492
17598
|
actionId,
|
|
17493
17599
|
actionName,
|
|
@@ -17504,6 +17610,8 @@ var ServiceAppManager = class {
|
|
|
17504
17610
|
*/
|
|
17505
17611
|
invokeInstalledServiceAction = async (appId, actionName, input = {}) => {
|
|
17506
17612
|
const { manifest, record } = await this.requireInstalledServiceApp(appId, actionName);
|
|
17613
|
+
const action = listServiceAppManifestActions(record, manifest).find((entry) => entry.name === actionName);
|
|
17614
|
+
await this.assertDocumentInputAccess(record, action?.risk ?? "dangerous", input);
|
|
17507
17615
|
return await this.jobManager.invoke({
|
|
17508
17616
|
actionId: buildServiceActionId(record.id, actionName),
|
|
17509
17617
|
actionName,
|
|
@@ -17514,6 +17622,24 @@ var ServiceAppManager = class {
|
|
|
17514
17622
|
entrySurface: "installed-app-cli"
|
|
17515
17623
|
});
|
|
17516
17624
|
};
|
|
17625
|
+
assertDocumentInputAccess = async (record, risk, input) => {
|
|
17626
|
+
const match = (typeof input.path === "string" ? input.path : void 0)?.match(DOCUMENT_GUEST_PATH_PATTERN);
|
|
17627
|
+
if (!match || !record.packageId || !this.params.assertDocumentAccess) return;
|
|
17628
|
+
const scopeId = decodeURIComponent(match[1]);
|
|
17629
|
+
const requestedMode = risk === "read" ? "read" : "read-write";
|
|
17630
|
+
try {
|
|
17631
|
+
await this.params.assertDocumentAccess(record.packageId, scopeId, requestedMode);
|
|
17632
|
+
} catch (error) {
|
|
17633
|
+
const code = typeof error === "object" && error !== null && "code" in error ? String(error.code) : "DOCUMENT_SCOPE_NOT_GRANTED";
|
|
17634
|
+
if (code === "DOCUMENT_SCOPE_NOT_GRANTED" || code === "DOCUMENT_SCOPE_MODE_INSUFFICIENT" || code === "DOCUMENT_SCOPE_UNAVAILABLE") throw new ServiceAppError(code, error instanceof Error ? error.message : code, {
|
|
17635
|
+
appId: record.packageId,
|
|
17636
|
+
scopeId,
|
|
17637
|
+
requestedMode,
|
|
17638
|
+
recoveryActions: code === "DOCUMENT_SCOPE_UNAVAILABLE" ? ["replace", "revoke"] : code === "DOCUMENT_SCOPE_MODE_INSUFFICIENT" ? ["upgrade"] : ["grant"]
|
|
17639
|
+
});
|
|
17640
|
+
throw error;
|
|
17641
|
+
}
|
|
17642
|
+
};
|
|
17517
17643
|
listVerificationRecords = async (filters = {}) => await this.jobManager.listVerificationRecords(filters);
|
|
17518
17644
|
exportVerificationRecords = async (filters = {}) => await this.jobManager.exportVerificationRecords(filters);
|
|
17519
17645
|
listServiceAppJobs = async (appId, params = {}) => await this.jobManager.list(appId, params.caller);
|
|
@@ -18870,7 +18996,7 @@ var SessionSettingsService = class {
|
|
|
18870
18996
|
existing = await this.options.getSessionRecord(sessionId);
|
|
18871
18997
|
}
|
|
18872
18998
|
if (!existing) return null;
|
|
18873
|
-
const metadata = await applySessionProjectMetadataPatch(applySessionSettingsMetadataPatch(existing.metadata ?? {}, patch), patch, this.options.
|
|
18999
|
+
const metadata = await applySessionProjectMetadataPatch(applySessionSettingsMetadataPatch(existing.metadata ?? {}, patch), patch, this.options.normalizeProjectContext);
|
|
18874
19000
|
return await this.options.setSessionMetadata(sessionId, metadata) ? await this.options.getSession(sessionId) : null;
|
|
18875
19001
|
};
|
|
18876
19002
|
};
|
|
@@ -18973,7 +19099,7 @@ var SessionManager = class {
|
|
|
18973
19099
|
createSession: this.createSession,
|
|
18974
19100
|
getSession: this.getSession,
|
|
18975
19101
|
getSessionRecord: this.getSessionRecord,
|
|
18976
|
-
|
|
19102
|
+
normalizeProjectContext: options.projectManager.normalizeSessionProjectContext,
|
|
18977
19103
|
setSessionMetadata: this.setSessionMetadata
|
|
18978
19104
|
});
|
|
18979
19105
|
}
|
|
@@ -19005,14 +19131,9 @@ var SessionManager = class {
|
|
|
19005
19131
|
title
|
|
19006
19132
|
});
|
|
19007
19133
|
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
19008
|
-
|
|
19134
|
+
let nextMetadata = mergeMetadataOverrides(metadata, metadataOverrides);
|
|
19009
19135
|
const requestedProjectRoot = projectRoot !== void 0 ? projectRoot : readProjectRoot(nextMetadata);
|
|
19010
|
-
if (requestedProjectRoot !== void 0) {
|
|
19011
|
-
const normalizedProjectRoot = await this.options.projectManager.normalizeSessionProjectRoot(requestedProjectRoot);
|
|
19012
|
-
delete nextMetadata.projectRoot;
|
|
19013
|
-
if (normalizedProjectRoot) nextMetadata.project_root = normalizedProjectRoot;
|
|
19014
|
-
else delete nextMetadata.project_root;
|
|
19015
|
-
}
|
|
19136
|
+
if (requestedProjectRoot !== void 0) nextMetadata = await applySessionProjectMetadataPatch(nextMetadata, { projectRoot: requestedProjectRoot }, this.options.projectManager.normalizeSessionProjectContext);
|
|
19016
19137
|
const agentId = readOptionalString$10(requestedAgentId) ?? readOptionalString$10(sourceRecord?.agentId) ?? BUILTIN_MAIN_AGENT_ID;
|
|
19017
19138
|
const sessionId = readOptionalString$10(requestedSessionId) ?? buildSessionId();
|
|
19018
19139
|
const inheritedContext = createSessionContextInheritance({
|
|
@@ -19197,6 +19318,7 @@ var SessionManager = class {
|
|
|
19197
19318
|
metadata: structuredClone(created.metadata ?? {}),
|
|
19198
19319
|
model: model ?? readOptionalMetadataString(created.metadata?.model) ?? readOptionalMetadataString(created.metadata?.preferred_model),
|
|
19199
19320
|
projectRoot: readProjectRoot(created.metadata),
|
|
19321
|
+
projectId: readProjectId(created.metadata),
|
|
19200
19322
|
workingDir: this.workingDirResolver.resolve({
|
|
19201
19323
|
agentId: created.agentId,
|
|
19202
19324
|
metadata: created.metadata
|
|
@@ -19218,6 +19340,7 @@ var SessionManager = class {
|
|
|
19218
19340
|
metadata: structuredClone(metadata),
|
|
19219
19341
|
model: readOptionalMetadataString(metadata.model) ?? readOptionalMetadataString(metadata.preferred_model),
|
|
19220
19342
|
projectRoot: readProjectRoot(metadata),
|
|
19343
|
+
projectId: readProjectId(metadata),
|
|
19221
19344
|
workingDir: this.workingDirResolver.resolve({
|
|
19222
19345
|
agentId: summary.agentId,
|
|
19223
19346
|
metadata
|
|
@@ -20375,14 +20498,20 @@ var ProjectManager = class {
|
|
|
20375
20498
|
});
|
|
20376
20499
|
};
|
|
20377
20500
|
normalizeSessionProjectRoot = async (value) => {
|
|
20501
|
+
return (await this.normalizeSessionProjectContext(value))?.rootPath ?? null;
|
|
20502
|
+
};
|
|
20503
|
+
normalizeSessionProjectContext = async (value) => {
|
|
20378
20504
|
if (value == null || typeof value === "string" && !value.trim()) return null;
|
|
20379
20505
|
const rootPath = await this.resolveExistingProjectRoot(value);
|
|
20380
20506
|
if (!rootPath) return null;
|
|
20381
|
-
await this.upsertProject({
|
|
20507
|
+
const project = await this.upsertProject({
|
|
20382
20508
|
name: basename(rootPath),
|
|
20383
20509
|
rootPath
|
|
20384
20510
|
});
|
|
20385
|
-
return
|
|
20511
|
+
return {
|
|
20512
|
+
projectId: project.id,
|
|
20513
|
+
rootPath: project.rootPath
|
|
20514
|
+
};
|
|
20386
20515
|
};
|
|
20387
20516
|
resolveExistingProjectRoot = async (value) => {
|
|
20388
20517
|
if (typeof value !== "string") throw new ProjectError("PROJECT_PATH_INVALID_TYPE", "project path must be a string or null");
|
|
@@ -20421,6 +20550,7 @@ var ProjectManager = class {
|
|
|
20421
20550
|
updatedAt: now
|
|
20422
20551
|
};
|
|
20423
20552
|
await this.store.save([...projects, project]);
|
|
20553
|
+
await this.options.onProjectRegistered?.(structuredClone(project));
|
|
20424
20554
|
return structuredClone(project);
|
|
20425
20555
|
};
|
|
20426
20556
|
assertNotDefaultWorkspace = async (rootPath) => {
|
|
@@ -20478,81 +20608,971 @@ var ProjectManager = class {
|
|
|
20478
20608
|
isMissingFileError = (error) => typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
20479
20609
|
};
|
|
20480
20610
|
//#endregion
|
|
20481
|
-
//#region src/
|
|
20482
|
-
const
|
|
20483
|
-
|
|
20484
|
-
|
|
20485
|
-
|
|
20486
|
-
|
|
20487
|
-
|
|
20611
|
+
//#region src/stores/sqlite-database.store.ts
|
|
20612
|
+
const require = createRequire(import.meta.url);
|
|
20613
|
+
const portableDatabases = /* @__PURE__ */ new Map();
|
|
20614
|
+
async function openSqliteDatabase(databasePath) {
|
|
20615
|
+
try {
|
|
20616
|
+
return new (require("node:sqlite")).DatabaseSync(databasePath);
|
|
20617
|
+
} catch (error) {
|
|
20618
|
+
if (!isMissingNodeSqlite(error)) throw error;
|
|
20619
|
+
return await openPortableDatabase(databasePath);
|
|
20620
|
+
}
|
|
20621
|
+
}
|
|
20622
|
+
function runSqliteTransaction(database, operation, mode = "DEFERRED") {
|
|
20623
|
+
database.exec(`BEGIN ${mode}`);
|
|
20624
|
+
try {
|
|
20625
|
+
const result = operation();
|
|
20626
|
+
database.exec("COMMIT");
|
|
20627
|
+
return result;
|
|
20628
|
+
} catch (error) {
|
|
20629
|
+
database.exec("ROLLBACK");
|
|
20630
|
+
throw error;
|
|
20631
|
+
}
|
|
20632
|
+
}
|
|
20633
|
+
function isMissingNodeSqlite(error) {
|
|
20634
|
+
const code = typeof error === "object" && error !== null ? error.code : void 0;
|
|
20635
|
+
return code === "ERR_UNKNOWN_BUILTIN_MODULE" || code === "MODULE_NOT_FOUND";
|
|
20636
|
+
}
|
|
20637
|
+
async function openPortableDatabase(databasePath) {
|
|
20638
|
+
const existing = portableDatabases.get(databasePath);
|
|
20639
|
+
if (existing) return (await existing).acquire();
|
|
20640
|
+
const opening = createPortableDatabase(databasePath).catch((error) => {
|
|
20641
|
+
portableDatabases.delete(databasePath);
|
|
20642
|
+
throw error;
|
|
20643
|
+
});
|
|
20644
|
+
portableDatabases.set(databasePath, opening);
|
|
20645
|
+
return (await opening).acquire();
|
|
20646
|
+
}
|
|
20647
|
+
async function createPortableDatabase(databasePath) {
|
|
20648
|
+
const initialize = (await import("sql.js")).default;
|
|
20649
|
+
const sqlite = await initialize({ locateFile: (file) => require.resolve(`sql.js/dist/${file}`) });
|
|
20650
|
+
return new PortableSqliteDatabase(databasePath, existsSync(databasePath) ? new sqlite.Database(readFileSync(databasePath)) : new sqlite.Database());
|
|
20651
|
+
}
|
|
20652
|
+
var PortableSqliteDatabase = class {
|
|
20653
|
+
inTransaction = false;
|
|
20654
|
+
dirty = false;
|
|
20655
|
+
leaseCount = 0;
|
|
20656
|
+
constructor(databasePath, database) {
|
|
20657
|
+
this.databasePath = databasePath;
|
|
20658
|
+
this.database = database;
|
|
20659
|
+
}
|
|
20660
|
+
acquire = () => {
|
|
20661
|
+
this.leaseCount += 1;
|
|
20662
|
+
return new PortableSqliteLease(this);
|
|
20663
|
+
};
|
|
20664
|
+
exec = (sql) => {
|
|
20665
|
+
const command = sql.trim().toUpperCase();
|
|
20666
|
+
this.database.run(sql);
|
|
20667
|
+
if (command.startsWith("BEGIN")) {
|
|
20668
|
+
this.inTransaction = true;
|
|
20669
|
+
return;
|
|
20670
|
+
}
|
|
20671
|
+
if (command.startsWith("ROLLBACK")) {
|
|
20672
|
+
this.inTransaction = false;
|
|
20673
|
+
this.dirty = false;
|
|
20674
|
+
return;
|
|
20675
|
+
}
|
|
20676
|
+
if (command.startsWith("COMMIT")) {
|
|
20677
|
+
this.inTransaction = false;
|
|
20678
|
+
this.persistIfDirty();
|
|
20679
|
+
return;
|
|
20680
|
+
}
|
|
20681
|
+
if (!command.startsWith("PRAGMA")) this.markDirty();
|
|
20682
|
+
};
|
|
20683
|
+
prepare = (sql) => new PortableSqliteStatement(this, sql);
|
|
20684
|
+
release = () => {
|
|
20685
|
+
this.leaseCount -= 1;
|
|
20686
|
+
if (this.leaseCount > 0) return;
|
|
20687
|
+
this.persistIfDirty();
|
|
20688
|
+
this.database.close();
|
|
20689
|
+
portableDatabases.delete(this.databasePath);
|
|
20690
|
+
};
|
|
20691
|
+
run = (sql, params) => {
|
|
20692
|
+
this.database.run(sql, normalizeParams(params));
|
|
20693
|
+
this.markDirty();
|
|
20694
|
+
};
|
|
20695
|
+
readRows = (sql, params) => {
|
|
20696
|
+
const statement = this.database.prepare(sql);
|
|
20697
|
+
try {
|
|
20698
|
+
bindStatement(statement, params);
|
|
20699
|
+
const rows = [];
|
|
20700
|
+
while (statement.step()) rows.push(statement.getAsObject());
|
|
20701
|
+
return rows;
|
|
20702
|
+
} finally {
|
|
20703
|
+
statement.free();
|
|
20704
|
+
}
|
|
20705
|
+
};
|
|
20706
|
+
markDirty = () => {
|
|
20707
|
+
this.dirty = true;
|
|
20708
|
+
if (!this.inTransaction) this.persistIfDirty();
|
|
20709
|
+
};
|
|
20710
|
+
persistIfDirty = () => {
|
|
20711
|
+
if (!this.dirty) return;
|
|
20712
|
+
const temporaryPath = `${this.databasePath}.tmp`;
|
|
20713
|
+
writeFileSync(temporaryPath, this.database.export());
|
|
20714
|
+
renameSync(temporaryPath, this.databasePath);
|
|
20715
|
+
this.dirty = false;
|
|
20716
|
+
};
|
|
20717
|
+
};
|
|
20718
|
+
var PortableSqliteLease = class {
|
|
20719
|
+
closed = false;
|
|
20720
|
+
constructor(owner) {
|
|
20721
|
+
this.owner = owner;
|
|
20722
|
+
}
|
|
20723
|
+
close = () => {
|
|
20724
|
+
if (this.closed) return;
|
|
20725
|
+
this.closed = true;
|
|
20726
|
+
this.owner.release();
|
|
20727
|
+
};
|
|
20728
|
+
exec = (sql) => this.owner.exec(sql);
|
|
20729
|
+
prepare = (sql) => this.owner.prepare(sql);
|
|
20730
|
+
};
|
|
20731
|
+
var PortableSqliteStatement = class {
|
|
20732
|
+
constructor(database, sql) {
|
|
20733
|
+
this.database = database;
|
|
20734
|
+
this.sql = sql;
|
|
20488
20735
|
}
|
|
20736
|
+
all = (...params) => this.database.readRows(this.sql, params);
|
|
20737
|
+
get = (...params) => this.all(...params)[0];
|
|
20738
|
+
run = (...params) => {
|
|
20739
|
+
this.database.run(this.sql, params);
|
|
20740
|
+
return {};
|
|
20741
|
+
};
|
|
20489
20742
|
};
|
|
20490
|
-
function
|
|
20491
|
-
|
|
20743
|
+
function bindStatement(statement, params) {
|
|
20744
|
+
const normalized = normalizeParams(params);
|
|
20745
|
+
if (Array.isArray(normalized) && normalized.length === 0) return;
|
|
20746
|
+
statement.bind(normalized);
|
|
20747
|
+
}
|
|
20748
|
+
function normalizeParams(params) {
|
|
20749
|
+
if (params.length !== 1 || !isRecord$9(params[0])) return params;
|
|
20750
|
+
return Object.fromEntries(Object.entries(params[0]).map(([key, value]) => [`@${key}`, value]));
|
|
20751
|
+
}
|
|
20752
|
+
function isRecord$9(value) {
|
|
20753
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20492
20754
|
}
|
|
20493
20755
|
//#endregion
|
|
20494
|
-
//#region src/features/projects/
|
|
20495
|
-
|
|
20496
|
-
|
|
20497
|
-
|
|
20498
|
-
|
|
20499
|
-
|
|
20500
|
-
const
|
|
20501
|
-
|
|
20502
|
-
|
|
20503
|
-
|
|
20504
|
-
|
|
20505
|
-
|
|
20506
|
-
|
|
20507
|
-
|
|
20756
|
+
//#region src/features/projects/stores/project-work-activity.store.ts
|
|
20757
|
+
var ProjectWorkActivityStore = class {
|
|
20758
|
+
constructor(db) {
|
|
20759
|
+
this.db = db;
|
|
20760
|
+
}
|
|
20761
|
+
listActivities = async (projectId, workItemId, options) => {
|
|
20762
|
+
const { cursor, limit } = options;
|
|
20763
|
+
const cursorSequence = cursor ? Number(cursor) : null;
|
|
20764
|
+
if (cursorSequence !== null && !Number.isInteger(cursorSequence)) throw new Error("PROJECT_WORK_ACTIVITY_CURSOR_INVALID");
|
|
20765
|
+
const rows = this.db().prepare(`SELECT rowid AS sequence, * FROM project_work_activities
|
|
20766
|
+
WHERE project_id = ? AND work_item_id = ? ${cursorSequence !== null ? "AND rowid < ?" : ""}
|
|
20767
|
+
ORDER BY rowid DESC LIMIT ?`).all(projectId, workItemId, ...cursorSequence !== null ? [cursorSequence] : [], limit + 1);
|
|
20768
|
+
const hasMore = rows.length > limit;
|
|
20769
|
+
const pageRows = rows.slice(0, limit);
|
|
20770
|
+
return {
|
|
20771
|
+
activities: pageRows.map(toActivity),
|
|
20772
|
+
nextCursor: hasMore ? String(pageRows.at(-1)?.sequence) : null
|
|
20773
|
+
};
|
|
20508
20774
|
};
|
|
20509
|
-
|
|
20510
|
-
|
|
20511
|
-
|
|
20512
|
-
|
|
20513
|
-
|
|
20514
|
-
|
|
20775
|
+
listArtifacts = async (projectId, workItemId) => this.db().prepare(`SELECT * FROM project_work_artifact_links
|
|
20776
|
+
WHERE project_id = ? AND work_item_id = ? ORDER BY created_at DESC`).all(projectId, workItemId).map(toArtifactLink);
|
|
20777
|
+
linkArtifact = async (params) => {
|
|
20778
|
+
const { actor, label, path, projectId, workItemId } = params;
|
|
20779
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
20780
|
+
const id = randomUUID();
|
|
20781
|
+
runSqliteTransaction(this.db(), () => {
|
|
20782
|
+
this.db().prepare(`INSERT INTO project_work_artifact_links
|
|
20783
|
+
(id, project_id, work_item_id, path, label, created_at) VALUES (?, ?, ?, ?, ?, ?)`).run(id, projectId, workItemId, path, label ?? null, now);
|
|
20784
|
+
this.insertActivity({
|
|
20785
|
+
projectId,
|
|
20786
|
+
workItemId,
|
|
20787
|
+
type: "artifact-linked",
|
|
20788
|
+
actor,
|
|
20789
|
+
details: {
|
|
20790
|
+
artifactLinkId: id,
|
|
20791
|
+
path
|
|
20792
|
+
},
|
|
20793
|
+
createdAt: now
|
|
20794
|
+
});
|
|
20795
|
+
}, "IMMEDIATE");
|
|
20796
|
+
return (await this.listArtifacts(projectId, workItemId)).find((link) => link.id === id);
|
|
20515
20797
|
};
|
|
20516
|
-
|
|
20798
|
+
unlinkArtifact = async (params) => {
|
|
20799
|
+
const { actor, artifactLinkId, projectId, workItemId } = params;
|
|
20800
|
+
const link = this.db().prepare(`SELECT * FROM project_work_artifact_links
|
|
20801
|
+
WHERE project_id = ? AND work_item_id = ? AND id = ? LIMIT 1`).get(projectId, workItemId, artifactLinkId);
|
|
20802
|
+
if (!link) return false;
|
|
20803
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
20804
|
+
runSqliteTransaction(this.db(), () => {
|
|
20805
|
+
this.db().prepare("DELETE FROM project_work_artifact_links WHERE project_id = ? AND work_item_id = ? AND id = ?").run(projectId, workItemId, artifactLinkId);
|
|
20806
|
+
this.insertActivity({
|
|
20807
|
+
projectId,
|
|
20808
|
+
workItemId,
|
|
20809
|
+
type: "artifact-unlinked",
|
|
20810
|
+
actor,
|
|
20811
|
+
details: {
|
|
20812
|
+
artifactLinkId: link.id,
|
|
20813
|
+
path: link.path
|
|
20814
|
+
},
|
|
20815
|
+
createdAt: now
|
|
20816
|
+
});
|
|
20817
|
+
}, "IMMEDIATE");
|
|
20818
|
+
return true;
|
|
20819
|
+
};
|
|
20820
|
+
insertActivity = (activity) => {
|
|
20821
|
+
this.db().prepare(`INSERT INTO project_work_activities
|
|
20822
|
+
(id, project_id, work_item_id, type, actor_json, details_json, created_at)
|
|
20823
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)`).run(randomUUID(), activity.projectId, activity.workItemId, activity.type, JSON.stringify(activity.actor), JSON.stringify(activity.details), activity.createdAt);
|
|
20824
|
+
};
|
|
20825
|
+
};
|
|
20826
|
+
function toActivity(row) {
|
|
20517
20827
|
return {
|
|
20518
|
-
|
|
20519
|
-
|
|
20520
|
-
|
|
20521
|
-
|
|
20522
|
-
|
|
20523
|
-
|
|
20524
|
-
|
|
20525
|
-
}
|
|
20526
|
-
issues.push(...collectUnknownKeyIssues(entry, [
|
|
20527
|
-
"id",
|
|
20528
|
-
"role",
|
|
20529
|
-
"source"
|
|
20530
|
-
], `project.context[${index}]`));
|
|
20531
|
-
const id = readString$7(entry.id);
|
|
20532
|
-
const role = readString$7(entry.role);
|
|
20533
|
-
const source = readString$7(entry.source);
|
|
20534
|
-
if (!id || !role || !source) {
|
|
20535
|
-
issues.push({
|
|
20536
|
-
code: "PROJECT_CONFIG_CONTEXT_INVALID",
|
|
20537
|
-
message: `project.context[${index}] requires id, role and source.`
|
|
20538
|
-
});
|
|
20539
|
-
return [];
|
|
20540
|
-
}
|
|
20541
|
-
return [{
|
|
20542
|
-
id,
|
|
20543
|
-
role,
|
|
20544
|
-
source
|
|
20545
|
-
}];
|
|
20546
|
-
}),
|
|
20547
|
-
issues
|
|
20828
|
+
id: row.id,
|
|
20829
|
+
projectId: row.project_id,
|
|
20830
|
+
workItemId: row.work_item_id,
|
|
20831
|
+
type: row.type,
|
|
20832
|
+
actor: JSON.parse(row.actor_json),
|
|
20833
|
+
details: JSON.parse(row.details_json),
|
|
20834
|
+
createdAt: row.created_at
|
|
20548
20835
|
};
|
|
20549
20836
|
}
|
|
20550
|
-
function
|
|
20551
|
-
|
|
20552
|
-
|
|
20553
|
-
|
|
20837
|
+
function toArtifactLink(row) {
|
|
20838
|
+
return {
|
|
20839
|
+
id: row.id,
|
|
20840
|
+
projectId: row.project_id,
|
|
20841
|
+
workItemId: row.work_item_id,
|
|
20842
|
+
path: row.path,
|
|
20843
|
+
label: row.label,
|
|
20844
|
+
createdAt: row.created_at
|
|
20554
20845
|
};
|
|
20555
|
-
|
|
20846
|
+
}
|
|
20847
|
+
//#endregion
|
|
20848
|
+
//#region src/features/projects/stores/project-work-state.store.ts
|
|
20849
|
+
const DEFAULT_PROJECT_WORK_STATES = [
|
|
20850
|
+
{
|
|
20851
|
+
name: "Backlog",
|
|
20852
|
+
category: "backlog",
|
|
20853
|
+
position: 0,
|
|
20854
|
+
isDefault: false
|
|
20855
|
+
},
|
|
20856
|
+
{
|
|
20857
|
+
name: "Planned",
|
|
20858
|
+
category: "unstarted",
|
|
20859
|
+
position: 1,
|
|
20860
|
+
isDefault: true
|
|
20861
|
+
},
|
|
20862
|
+
{
|
|
20863
|
+
name: "In Progress",
|
|
20864
|
+
category: "started",
|
|
20865
|
+
position: 2,
|
|
20866
|
+
isDefault: false
|
|
20867
|
+
},
|
|
20868
|
+
{
|
|
20869
|
+
name: "In Review",
|
|
20870
|
+
category: "started",
|
|
20871
|
+
position: 3,
|
|
20872
|
+
isDefault: false
|
|
20873
|
+
},
|
|
20874
|
+
{
|
|
20875
|
+
name: "Awaiting Acceptance",
|
|
20876
|
+
category: "started",
|
|
20877
|
+
position: 4,
|
|
20878
|
+
isDefault: false
|
|
20879
|
+
},
|
|
20880
|
+
{
|
|
20881
|
+
name: "Completed",
|
|
20882
|
+
category: "completed",
|
|
20883
|
+
position: 5,
|
|
20884
|
+
isDefault: false
|
|
20885
|
+
},
|
|
20886
|
+
{
|
|
20887
|
+
name: "Canceled",
|
|
20888
|
+
category: "canceled",
|
|
20889
|
+
position: 6,
|
|
20890
|
+
isDefault: false
|
|
20891
|
+
}
|
|
20892
|
+
];
|
|
20893
|
+
var ProjectWorkStateStore = class {
|
|
20894
|
+
constructor(db, insertActivity) {
|
|
20895
|
+
this.db = db;
|
|
20896
|
+
this.insertActivity = insertActivity;
|
|
20897
|
+
}
|
|
20898
|
+
ensureProject = async (projectId) => {
|
|
20899
|
+
if (this.db().prepare("SELECT COUNT(*) AS total FROM project_work_states WHERE project_id = ?").get(projectId).total > 0) return;
|
|
20900
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
20901
|
+
runSqliteTransaction(this.db(), () => {
|
|
20902
|
+
if (this.db().prepare("SELECT COUNT(*) AS total FROM project_work_states WHERE project_id = ?").get(projectId).total > 0) return;
|
|
20903
|
+
const insert = this.db().prepare(`INSERT INTO project_work_states
|
|
20904
|
+
(id, project_id, name, category, position, is_default, created_at, updated_at)
|
|
20905
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);
|
|
20906
|
+
for (const state of DEFAULT_PROJECT_WORK_STATES) insert.run(randomUUID(), projectId, state.name, state.category, state.position, state.isDefault ? 1 : 0, now, now);
|
|
20907
|
+
}, "IMMEDIATE");
|
|
20908
|
+
};
|
|
20909
|
+
listStates = async (projectId) => this.db().prepare("SELECT * FROM project_work_states WHERE project_id = ? ORDER BY position, created_at").all(projectId).map(toState);
|
|
20910
|
+
getState = async (projectId, stateId) => {
|
|
20911
|
+
const row = this.db().prepare("SELECT * FROM project_work_states WHERE project_id = ? AND id = ? LIMIT 1").get(projectId, stateId);
|
|
20912
|
+
return row ? toState(row) : null;
|
|
20913
|
+
};
|
|
20914
|
+
createState = async (projectId, input) => {
|
|
20915
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
20916
|
+
const id = randomUUID();
|
|
20917
|
+
const position = input.position ?? this.nextStatePosition(projectId);
|
|
20918
|
+
runSqliteTransaction(this.db(), () => {
|
|
20919
|
+
if (input.isDefault) this.clearDefaultState(projectId);
|
|
20920
|
+
this.db().prepare(`INSERT INTO project_work_states
|
|
20921
|
+
(id, project_id, name, category, position, is_default, created_at, updated_at)
|
|
20922
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(id, projectId, input.name, input.category, position, input.isDefault ? 1 : 0, now, now);
|
|
20923
|
+
}, "IMMEDIATE");
|
|
20924
|
+
return await this.getState(projectId, id);
|
|
20925
|
+
};
|
|
20926
|
+
updateState = async (projectId, stateId, input) => {
|
|
20927
|
+
const current = await this.getState(projectId, stateId);
|
|
20928
|
+
if (!current) return null;
|
|
20929
|
+
const next = {
|
|
20930
|
+
...current,
|
|
20931
|
+
...input,
|
|
20932
|
+
updatedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
20933
|
+
};
|
|
20934
|
+
runSqliteTransaction(this.db(), () => {
|
|
20935
|
+
if (next.isDefault) this.clearDefaultState(projectId);
|
|
20936
|
+
this.db().prepare(`UPDATE project_work_states
|
|
20937
|
+
SET name = ?, category = ?, position = ?, is_default = ?, updated_at = ?
|
|
20938
|
+
WHERE project_id = ? AND id = ?`).run(next.name, next.category, next.position, next.isDefault ? 1 : 0, next.updatedAt, projectId, stateId);
|
|
20939
|
+
}, "IMMEDIATE");
|
|
20940
|
+
return await this.getState(projectId, stateId);
|
|
20941
|
+
};
|
|
20942
|
+
deleteState = async (projectId, stateId, migrateToStateId, actor) => {
|
|
20943
|
+
const current = await this.getState(projectId, stateId);
|
|
20944
|
+
if (!current) return false;
|
|
20945
|
+
runSqliteTransaction(this.db(), () => {
|
|
20946
|
+
if (this.db().prepare("SELECT COUNT(*) AS total FROM project_work_items WHERE project_id = ? AND state_id = ?").get(projectId, stateId).total > 0 && !migrateToStateId) throw new Error("PROJECT_WORK_STATE_IN_USE");
|
|
20947
|
+
if (migrateToStateId) this.migrateItems(projectId, stateId, migrateToStateId, actor);
|
|
20948
|
+
this.db().prepare("DELETE FROM project_work_states WHERE project_id = ? AND id = ?").run(projectId, stateId);
|
|
20949
|
+
if (current.isDefault) this.assignReplacementDefault(projectId, migrateToStateId);
|
|
20950
|
+
}, "IMMEDIATE");
|
|
20951
|
+
return true;
|
|
20952
|
+
};
|
|
20953
|
+
migrateItems = (projectId, stateId, migrateToStateId, actor) => {
|
|
20954
|
+
const affected = this.db().prepare("SELECT id FROM project_work_items WHERE project_id = ? AND state_id = ?").all(projectId, stateId);
|
|
20955
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
20956
|
+
this.db().prepare(`UPDATE project_work_items
|
|
20957
|
+
SET state_id = ?, version = version + 1, updated_at = ?
|
|
20958
|
+
WHERE project_id = ? AND state_id = ?`).run(migrateToStateId, now, projectId, stateId);
|
|
20959
|
+
for (const item of affected) this.insertActivity({
|
|
20960
|
+
projectId,
|
|
20961
|
+
workItemId: item.id,
|
|
20962
|
+
type: "state-changed",
|
|
20963
|
+
actor,
|
|
20964
|
+
details: {
|
|
20965
|
+
fromStateId: stateId,
|
|
20966
|
+
toStateId: migrateToStateId,
|
|
20967
|
+
reason: "state-deleted"
|
|
20968
|
+
},
|
|
20969
|
+
createdAt: now
|
|
20970
|
+
});
|
|
20971
|
+
};
|
|
20972
|
+
assignReplacementDefault = (projectId, migrateToStateId) => {
|
|
20973
|
+
const replacement = migrateToStateId ?? this.db().prepare("SELECT id FROM project_work_states WHERE project_id = ? ORDER BY position LIMIT 1").get(projectId)?.id;
|
|
20974
|
+
if (replacement) this.db().prepare("UPDATE project_work_states SET is_default = 1 WHERE project_id = ? AND id = ?").run(projectId, replacement);
|
|
20975
|
+
};
|
|
20976
|
+
nextStatePosition = (projectId) => {
|
|
20977
|
+
return this.db().prepare("SELECT COALESCE(MAX(position), -1) + 1 AS position FROM project_work_states WHERE project_id = ?").get(projectId).position;
|
|
20978
|
+
};
|
|
20979
|
+
clearDefaultState = (projectId) => {
|
|
20980
|
+
this.db().prepare("UPDATE project_work_states SET is_default = 0 WHERE project_id = ?").run(projectId);
|
|
20981
|
+
};
|
|
20982
|
+
};
|
|
20983
|
+
function toState(row) {
|
|
20984
|
+
return {
|
|
20985
|
+
id: row.id,
|
|
20986
|
+
projectId: row.project_id,
|
|
20987
|
+
name: row.name,
|
|
20988
|
+
category: row.category,
|
|
20989
|
+
position: row.position,
|
|
20990
|
+
isDefault: Boolean(row.is_default),
|
|
20991
|
+
createdAt: row.created_at,
|
|
20992
|
+
updatedAt: row.updated_at
|
|
20993
|
+
};
|
|
20994
|
+
}
|
|
20995
|
+
//#endregion
|
|
20996
|
+
//#region src/features/projects/stores/project-work.store.ts
|
|
20997
|
+
var ProjectWorkStore = class {
|
|
20998
|
+
database = null;
|
|
20999
|
+
readyPromise = null;
|
|
21000
|
+
activities = new ProjectWorkActivityStore(() => this.db());
|
|
21001
|
+
states = new ProjectWorkStateStore(() => this.db(), this.activities.insertActivity);
|
|
21002
|
+
constructor(databasePath) {
|
|
21003
|
+
this.databasePath = databasePath;
|
|
21004
|
+
}
|
|
21005
|
+
initialize = async () => await this.ensureReady();
|
|
21006
|
+
close = () => {
|
|
21007
|
+
this.database?.close();
|
|
21008
|
+
this.database = null;
|
|
21009
|
+
this.readyPromise = null;
|
|
21010
|
+
};
|
|
21011
|
+
ensureProject = async (projectId) => {
|
|
21012
|
+
await this.ensureReady();
|
|
21013
|
+
await this.states.ensureProject(projectId);
|
|
21014
|
+
};
|
|
21015
|
+
listStates = async (projectId) => {
|
|
21016
|
+
await this.ensureReady();
|
|
21017
|
+
return await this.states.listStates(projectId);
|
|
21018
|
+
};
|
|
21019
|
+
getState = async (projectId, stateId) => {
|
|
21020
|
+
await this.ensureReady();
|
|
21021
|
+
return await this.states.getState(projectId, stateId);
|
|
21022
|
+
};
|
|
21023
|
+
createState = async (projectId, input) => {
|
|
21024
|
+
await this.ensureReady();
|
|
21025
|
+
return await this.states.createState(projectId, input);
|
|
21026
|
+
};
|
|
21027
|
+
updateState = async (projectId, stateId, input) => {
|
|
21028
|
+
await this.ensureReady();
|
|
21029
|
+
return await this.states.updateState(projectId, stateId, input);
|
|
21030
|
+
};
|
|
21031
|
+
deleteState = async (projectId, stateId, migrateToStateId, actor) => {
|
|
21032
|
+
await this.ensureReady();
|
|
21033
|
+
return await this.states.deleteState(projectId, stateId, migrateToStateId, actor);
|
|
21034
|
+
};
|
|
21035
|
+
listItems = async (projectId, includeDeleted = false) => {
|
|
21036
|
+
await this.ensureReady();
|
|
21037
|
+
return this.db().prepare(`SELECT * FROM project_work_items
|
|
21038
|
+
WHERE project_id = ? ${includeDeleted ? "" : "AND deleted_at IS NULL"}
|
|
21039
|
+
ORDER BY updated_at DESC, created_at DESC`).all(projectId).map(toItem);
|
|
21040
|
+
};
|
|
21041
|
+
getItem = async (projectId, workItemId) => {
|
|
21042
|
+
await this.ensureReady();
|
|
21043
|
+
const row = this.db().prepare("SELECT * FROM project_work_items WHERE project_id = ? AND id = ? LIMIT 1").get(projectId, workItemId);
|
|
21044
|
+
return row ? toItem(row) : null;
|
|
21045
|
+
};
|
|
21046
|
+
createItem = async (params) => {
|
|
21047
|
+
await this.ensureReady();
|
|
21048
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
21049
|
+
const id = randomUUID();
|
|
21050
|
+
runSqliteTransaction(this.db(), () => {
|
|
21051
|
+
this.db().prepare(`INSERT INTO project_work_items
|
|
21052
|
+
(id, project_id, title, description, state_id, attention, version, created_at, updated_at, deleted_at)
|
|
21053
|
+
VALUES (?, ?, ?, ?, ?, ?, 1, ?, ?, NULL)`).run(id, params.projectId, params.input.title, params.input.description ?? "", params.stateId, params.input.attention ?? "none", now, now);
|
|
21054
|
+
this.activities.insertActivity({
|
|
21055
|
+
projectId: params.projectId,
|
|
21056
|
+
workItemId: id,
|
|
21057
|
+
type: "created",
|
|
21058
|
+
actor: params.actor,
|
|
21059
|
+
details: { stateId: params.stateId },
|
|
21060
|
+
createdAt: now
|
|
21061
|
+
});
|
|
21062
|
+
}, "IMMEDIATE");
|
|
21063
|
+
return await this.getItem(params.projectId, id);
|
|
21064
|
+
};
|
|
21065
|
+
updateItem = async (params) => {
|
|
21066
|
+
await this.ensureReady();
|
|
21067
|
+
const { actor, input, projectId, workItemId } = params;
|
|
21068
|
+
const current = await this.getItem(projectId, workItemId);
|
|
21069
|
+
if (!current) return null;
|
|
21070
|
+
if (input.expectedVersion !== void 0 && input.expectedVersion !== current.version) throw new Error("PROJECT_WORK_VERSION_CONFLICT");
|
|
21071
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
21072
|
+
const next = {
|
|
21073
|
+
...current,
|
|
21074
|
+
...input
|
|
21075
|
+
};
|
|
21076
|
+
runSqliteTransaction(this.db(), () => {
|
|
21077
|
+
this.db().prepare(`UPDATE project_work_items SET title = ?, description = ?, state_id = ?, attention = ?,
|
|
21078
|
+
version = version + 1, updated_at = ? WHERE project_id = ? AND id = ?`).run(next.title, next.description, next.stateId, next.attention, now, projectId, workItemId);
|
|
21079
|
+
const stateChanged = next.stateId !== current.stateId;
|
|
21080
|
+
this.activities.insertActivity({
|
|
21081
|
+
projectId,
|
|
21082
|
+
workItemId,
|
|
21083
|
+
type: stateChanged ? "state-changed" : "updated",
|
|
21084
|
+
actor,
|
|
21085
|
+
details: stateChanged ? {
|
|
21086
|
+
fromStateId: current.stateId,
|
|
21087
|
+
toStateId: next.stateId
|
|
21088
|
+
} : changedItemFields(current, next),
|
|
21089
|
+
createdAt: now
|
|
21090
|
+
});
|
|
21091
|
+
}, "IMMEDIATE");
|
|
21092
|
+
return await this.getItem(projectId, workItemId);
|
|
21093
|
+
};
|
|
21094
|
+
setDeleted = async (params) => {
|
|
21095
|
+
await this.ensureReady();
|
|
21096
|
+
const { actor, deleted, projectId, workItemId } = params;
|
|
21097
|
+
if (!await this.getItem(projectId, workItemId)) return null;
|
|
21098
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
21099
|
+
runSqliteTransaction(this.db(), () => {
|
|
21100
|
+
this.db().prepare(`UPDATE project_work_items SET deleted_at = ?, version = version + 1, updated_at = ?
|
|
21101
|
+
WHERE project_id = ? AND id = ?`).run(deleted ? now : null, now, projectId, workItemId);
|
|
21102
|
+
this.activities.insertActivity({
|
|
21103
|
+
projectId,
|
|
21104
|
+
workItemId,
|
|
21105
|
+
type: deleted ? "deleted" : "restored",
|
|
21106
|
+
actor,
|
|
21107
|
+
details: {},
|
|
21108
|
+
createdAt: now
|
|
21109
|
+
});
|
|
21110
|
+
}, "IMMEDIATE");
|
|
21111
|
+
return await this.getItem(projectId, workItemId);
|
|
21112
|
+
};
|
|
21113
|
+
listActivities = async (projectId, workItemId, options) => {
|
|
21114
|
+
await this.ensureReady();
|
|
21115
|
+
return await this.activities.listActivities(projectId, workItemId, options);
|
|
21116
|
+
};
|
|
21117
|
+
listArtifacts = async (projectId, workItemId) => {
|
|
21118
|
+
await this.ensureReady();
|
|
21119
|
+
return await this.activities.listArtifacts(projectId, workItemId);
|
|
21120
|
+
};
|
|
21121
|
+
linkArtifact = async (params) => {
|
|
21122
|
+
await this.ensureReady();
|
|
21123
|
+
return await this.activities.linkArtifact(params);
|
|
21124
|
+
};
|
|
21125
|
+
unlinkArtifact = async (params) => {
|
|
21126
|
+
await this.ensureReady();
|
|
21127
|
+
return await this.activities.unlinkArtifact(params);
|
|
21128
|
+
};
|
|
21129
|
+
ensureReady = async () => {
|
|
21130
|
+
this.readyPromise ??= this.open();
|
|
21131
|
+
await this.readyPromise;
|
|
21132
|
+
};
|
|
21133
|
+
open = async () => {
|
|
21134
|
+
await mkdir(dirname(this.databasePath), { recursive: true });
|
|
21135
|
+
this.database = await openSqliteDatabase(this.databasePath);
|
|
21136
|
+
this.database.exec(`
|
|
21137
|
+
PRAGMA busy_timeout = 10000;
|
|
21138
|
+
PRAGMA journal_mode = WAL;
|
|
21139
|
+
PRAGMA synchronous = NORMAL;
|
|
21140
|
+
PRAGMA foreign_keys = ON;
|
|
21141
|
+
CREATE TABLE IF NOT EXISTS project_work_states (
|
|
21142
|
+
id TEXT PRIMARY KEY,
|
|
21143
|
+
project_id TEXT NOT NULL,
|
|
21144
|
+
name TEXT NOT NULL,
|
|
21145
|
+
category TEXT NOT NULL,
|
|
21146
|
+
position INTEGER NOT NULL,
|
|
21147
|
+
is_default INTEGER NOT NULL DEFAULT 0,
|
|
21148
|
+
created_at TEXT NOT NULL,
|
|
21149
|
+
updated_at TEXT NOT NULL,
|
|
21150
|
+
UNIQUE(project_id, name)
|
|
21151
|
+
);
|
|
21152
|
+
CREATE TABLE IF NOT EXISTS project_work_items (
|
|
21153
|
+
id TEXT PRIMARY KEY,
|
|
21154
|
+
project_id TEXT NOT NULL,
|
|
21155
|
+
title TEXT NOT NULL,
|
|
21156
|
+
description TEXT NOT NULL DEFAULT '',
|
|
21157
|
+
state_id TEXT NOT NULL,
|
|
21158
|
+
attention TEXT NOT NULL DEFAULT 'none',
|
|
21159
|
+
version INTEGER NOT NULL DEFAULT 1,
|
|
21160
|
+
created_at TEXT NOT NULL,
|
|
21161
|
+
updated_at TEXT NOT NULL,
|
|
21162
|
+
deleted_at TEXT,
|
|
21163
|
+
FOREIGN KEY(state_id) REFERENCES project_work_states(id)
|
|
21164
|
+
);
|
|
21165
|
+
CREATE TABLE IF NOT EXISTS project_work_activities (
|
|
21166
|
+
id TEXT PRIMARY KEY,
|
|
21167
|
+
project_id TEXT NOT NULL,
|
|
21168
|
+
work_item_id TEXT NOT NULL,
|
|
21169
|
+
type TEXT NOT NULL,
|
|
21170
|
+
actor_json TEXT NOT NULL,
|
|
21171
|
+
details_json TEXT NOT NULL,
|
|
21172
|
+
created_at TEXT NOT NULL,
|
|
21173
|
+
FOREIGN KEY(work_item_id) REFERENCES project_work_items(id)
|
|
21174
|
+
);
|
|
21175
|
+
CREATE TABLE IF NOT EXISTS project_work_artifact_links (
|
|
21176
|
+
id TEXT PRIMARY KEY,
|
|
21177
|
+
project_id TEXT NOT NULL,
|
|
21178
|
+
work_item_id TEXT NOT NULL,
|
|
21179
|
+
path TEXT NOT NULL,
|
|
21180
|
+
label TEXT,
|
|
21181
|
+
created_at TEXT NOT NULL,
|
|
21182
|
+
UNIQUE(project_id, work_item_id, path),
|
|
21183
|
+
FOREIGN KEY(work_item_id) REFERENCES project_work_items(id)
|
|
21184
|
+
);
|
|
21185
|
+
CREATE INDEX IF NOT EXISTS project_work_items_list_idx
|
|
21186
|
+
ON project_work_items(project_id, deleted_at, updated_at);
|
|
21187
|
+
CREATE INDEX IF NOT EXISTS project_work_activity_timeline_idx
|
|
21188
|
+
ON project_work_activities(project_id, work_item_id, created_at);
|
|
21189
|
+
`);
|
|
21190
|
+
};
|
|
21191
|
+
db = () => {
|
|
21192
|
+
if (!this.database) throw new Error("Project work database is not initialized.");
|
|
21193
|
+
return this.database;
|
|
21194
|
+
};
|
|
21195
|
+
};
|
|
21196
|
+
function toItem(row) {
|
|
21197
|
+
return {
|
|
21198
|
+
id: row.id,
|
|
21199
|
+
projectId: row.project_id,
|
|
21200
|
+
title: row.title,
|
|
21201
|
+
description: row.description,
|
|
21202
|
+
stateId: row.state_id,
|
|
21203
|
+
attention: row.attention,
|
|
21204
|
+
version: row.version,
|
|
21205
|
+
createdAt: row.created_at,
|
|
21206
|
+
updatedAt: row.updated_at,
|
|
21207
|
+
deletedAt: row.deleted_at
|
|
21208
|
+
};
|
|
21209
|
+
}
|
|
21210
|
+
function changedItemFields(current, next) {
|
|
21211
|
+
const fields = {};
|
|
21212
|
+
if (current.title !== next.title) fields.title = {
|
|
21213
|
+
from: current.title,
|
|
21214
|
+
to: next.title
|
|
21215
|
+
};
|
|
21216
|
+
if (current.description !== next.description) fields.descriptionChanged = true;
|
|
21217
|
+
if (current.attention !== next.attention) fields.attention = {
|
|
21218
|
+
from: current.attention,
|
|
21219
|
+
to: next.attention
|
|
21220
|
+
};
|
|
21221
|
+
return fields;
|
|
21222
|
+
}
|
|
21223
|
+
//#endregion
|
|
21224
|
+
//#region src/features/projects/types/project-work-error.types.ts
|
|
21225
|
+
var ProjectWorkError = class extends Error {
|
|
21226
|
+
constructor(code, message) {
|
|
21227
|
+
super(message);
|
|
21228
|
+
this.code = code;
|
|
21229
|
+
this.name = "ProjectWorkError";
|
|
21230
|
+
}
|
|
21231
|
+
};
|
|
21232
|
+
function isProjectWorkError(error) {
|
|
21233
|
+
return error instanceof ProjectWorkError;
|
|
21234
|
+
}
|
|
21235
|
+
//#endregion
|
|
21236
|
+
//#region src/features/projects/types/project-work.types.ts
|
|
21237
|
+
const PROJECT_WORK_STATE_CATEGORIES = [
|
|
21238
|
+
"backlog",
|
|
21239
|
+
"unstarted",
|
|
21240
|
+
"started",
|
|
21241
|
+
"completed",
|
|
21242
|
+
"canceled"
|
|
21243
|
+
];
|
|
21244
|
+
const PROJECT_WORK_ATTENTION_VALUES = [
|
|
21245
|
+
"none",
|
|
21246
|
+
"blocked",
|
|
21247
|
+
"awaiting-user"
|
|
21248
|
+
];
|
|
21249
|
+
//#endregion
|
|
21250
|
+
//#region src/features/projects/managers/project-work.manager.ts
|
|
21251
|
+
var ProjectWorkManager = class {
|
|
21252
|
+
store;
|
|
21253
|
+
constructor(options) {
|
|
21254
|
+
this.options = options;
|
|
21255
|
+
this.store = new ProjectWorkStore(options.databasePath);
|
|
21256
|
+
}
|
|
21257
|
+
initialize = async () => {
|
|
21258
|
+
await this.store.initialize();
|
|
21259
|
+
for (const project of await this.options.projectManager.listProjects()) await this.store.ensureProject(project.id);
|
|
21260
|
+
};
|
|
21261
|
+
dispose = () => this.store.close();
|
|
21262
|
+
ensureProject = async (projectId) => {
|
|
21263
|
+
await this.requireProject(projectId);
|
|
21264
|
+
await this.store.ensureProject(projectId);
|
|
21265
|
+
};
|
|
21266
|
+
list = async (projectId, includeDeleted = false) => {
|
|
21267
|
+
await this.ensureProject(projectId);
|
|
21268
|
+
const [states, items] = await Promise.all([this.store.listStates(projectId), this.store.listItems(projectId, includeDeleted)]);
|
|
21269
|
+
const stateById = new Map(states.map((state) => [state.id, state]));
|
|
21270
|
+
const detailed = await Promise.all(items.map(async (item) => ({
|
|
21271
|
+
...item,
|
|
21272
|
+
state: this.requireMappedState(stateById, item.stateId),
|
|
21273
|
+
artifacts: await this.store.listArtifacts(projectId, item.id)
|
|
21274
|
+
})));
|
|
21275
|
+
return {
|
|
21276
|
+
items: detailed,
|
|
21277
|
+
states,
|
|
21278
|
+
total: detailed.length
|
|
21279
|
+
};
|
|
21280
|
+
};
|
|
21281
|
+
summary = async (projectId) => {
|
|
21282
|
+
const { items } = await this.list(projectId);
|
|
21283
|
+
return {
|
|
21284
|
+
total: items.length,
|
|
21285
|
+
active: items.filter((item) => item.state.category !== "completed" && item.state.category !== "canceled").length,
|
|
21286
|
+
completed: items.filter((item) => item.state.category === "completed").length,
|
|
21287
|
+
attention: items.filter((item) => item.attention !== "none").length,
|
|
21288
|
+
updatedAt: items[0]?.updatedAt ?? null
|
|
21289
|
+
};
|
|
21290
|
+
};
|
|
21291
|
+
get = async (projectId, workItemId) => {
|
|
21292
|
+
await this.ensureProject(projectId);
|
|
21293
|
+
const item = await this.store.getItem(projectId, workItemId);
|
|
21294
|
+
if (!item) throw new ProjectWorkError("PROJECT_WORK_ITEM_NOT_FOUND", "work item was not found");
|
|
21295
|
+
const state = await this.store.getState(projectId, item.stateId);
|
|
21296
|
+
if (!state) throw new ProjectWorkError("PROJECT_WORK_STATE_NOT_FOUND", "work item state was not found");
|
|
21297
|
+
return {
|
|
21298
|
+
...item,
|
|
21299
|
+
state,
|
|
21300
|
+
artifacts: await this.store.listArtifacts(projectId, workItemId)
|
|
21301
|
+
};
|
|
21302
|
+
};
|
|
21303
|
+
create = async (projectId, input, actor) => {
|
|
21304
|
+
await this.ensureProject(projectId);
|
|
21305
|
+
const title = this.requireName(input.title, "work item title");
|
|
21306
|
+
const attention = input.attention ?? "none";
|
|
21307
|
+
this.assertAttention(attention);
|
|
21308
|
+
const states = await this.store.listStates(projectId);
|
|
21309
|
+
const state = input.stateId ? states.find((entry) => entry.id === input.stateId) : states.find((entry) => entry.isDefault);
|
|
21310
|
+
if (!state) throw new ProjectWorkError("PROJECT_WORK_STATE_NOT_FOUND", "work item state was not found");
|
|
21311
|
+
const item = await this.store.createItem({
|
|
21312
|
+
projectId,
|
|
21313
|
+
actor: this.normalizeActor(actor),
|
|
21314
|
+
stateId: state.id,
|
|
21315
|
+
input: {
|
|
21316
|
+
title,
|
|
21317
|
+
description: input.description?.trim() ?? "",
|
|
21318
|
+
attention
|
|
21319
|
+
}
|
|
21320
|
+
});
|
|
21321
|
+
await this.emit(projectId, "created", item.id);
|
|
21322
|
+
return await this.get(projectId, item.id);
|
|
21323
|
+
};
|
|
21324
|
+
update = async (projectId, workItemId, input, actor) => {
|
|
21325
|
+
await this.ensureProject(projectId);
|
|
21326
|
+
const current = await this.get(projectId, workItemId);
|
|
21327
|
+
if (input.expectedVersion !== void 0 && input.expectedVersion !== current.version) throw new ProjectWorkError("PROJECT_WORK_VERSION_CONFLICT", "work item changed since it was loaded");
|
|
21328
|
+
const normalizedInput = {
|
|
21329
|
+
...input,
|
|
21330
|
+
...input.title !== void 0 ? { title: this.requireName(input.title, "work item title") } : {},
|
|
21331
|
+
...input.description !== void 0 ? { description: input.description.trim() } : {}
|
|
21332
|
+
};
|
|
21333
|
+
if (normalizedInput.attention !== void 0) this.assertAttention(normalizedInput.attention);
|
|
21334
|
+
if (normalizedInput.stateId !== void 0 && !await this.store.getState(projectId, normalizedInput.stateId)) throw new ProjectWorkError("PROJECT_WORK_STATE_NOT_FOUND", "work item state was not found");
|
|
21335
|
+
if ((normalizedInput.title === void 0 || normalizedInput.title === current.title) && (normalizedInput.description === void 0 || normalizedInput.description === current.description) && (normalizedInput.stateId === void 0 || normalizedInput.stateId === current.stateId) && (normalizedInput.attention === void 0 || normalizedInput.attention === current.attention)) return current;
|
|
21336
|
+
let item;
|
|
21337
|
+
try {
|
|
21338
|
+
item = await this.store.updateItem({
|
|
21339
|
+
projectId,
|
|
21340
|
+
workItemId,
|
|
21341
|
+
input: normalizedInput,
|
|
21342
|
+
actor: this.normalizeActor(actor)
|
|
21343
|
+
});
|
|
21344
|
+
} catch (error) {
|
|
21345
|
+
if (error instanceof Error && error.message === "PROJECT_WORK_VERSION_CONFLICT") throw new ProjectWorkError("PROJECT_WORK_VERSION_CONFLICT", "work item changed since it was loaded");
|
|
21346
|
+
throw error;
|
|
21347
|
+
}
|
|
21348
|
+
if (!item) throw new ProjectWorkError("PROJECT_WORK_ITEM_NOT_FOUND", "work item was not found");
|
|
21349
|
+
await this.emit(projectId, "updated", workItemId);
|
|
21350
|
+
return await this.get(projectId, workItemId);
|
|
21351
|
+
};
|
|
21352
|
+
delete = async (projectId, workItemId, actor) => {
|
|
21353
|
+
return await this.setDeleted(projectId, workItemId, true, actor);
|
|
21354
|
+
};
|
|
21355
|
+
restore = async (projectId, workItemId, actor) => {
|
|
21356
|
+
return await this.setDeleted(projectId, workItemId, false, actor);
|
|
21357
|
+
};
|
|
21358
|
+
listActivities = async (projectId, workItemId, options = {}) => {
|
|
21359
|
+
await this.get(projectId, workItemId);
|
|
21360
|
+
if (options.cursor && !/^\d+$/.test(options.cursor)) throw new ProjectWorkError("PROJECT_WORK_VALIDATION_FAILED", "work item activity cursor is invalid");
|
|
21361
|
+
return await this.store.listActivities(projectId, workItemId, {
|
|
21362
|
+
...options.cursor ? { cursor: options.cursor } : {},
|
|
21363
|
+
limit: Math.min(100, Math.max(1, options.limit ?? 50))
|
|
21364
|
+
});
|
|
21365
|
+
};
|
|
21366
|
+
listStates = async (projectId) => {
|
|
21367
|
+
await this.ensureProject(projectId);
|
|
21368
|
+
return await this.store.listStates(projectId);
|
|
21369
|
+
};
|
|
21370
|
+
createState = async (projectId, input) => {
|
|
21371
|
+
await this.ensureProject(projectId);
|
|
21372
|
+
const normalized = {
|
|
21373
|
+
...input,
|
|
21374
|
+
name: this.requireName(input.name, "state name")
|
|
21375
|
+
};
|
|
21376
|
+
this.assertCategory(normalized.category);
|
|
21377
|
+
if ((await this.store.listStates(projectId)).some((state) => state.name.toLocaleLowerCase() === normalized.name.toLocaleLowerCase())) throw new ProjectWorkError("PROJECT_WORK_STATE_INVALID", "state name is already in use");
|
|
21378
|
+
const state = await this.store.createState(projectId, normalized);
|
|
21379
|
+
await this.emit(projectId, "state-config");
|
|
21380
|
+
return state;
|
|
21381
|
+
};
|
|
21382
|
+
updateState = async (projectId, stateId, input) => {
|
|
21383
|
+
await this.ensureProject(projectId);
|
|
21384
|
+
const normalizedInput = {
|
|
21385
|
+
...input,
|
|
21386
|
+
...input.name !== void 0 ? { name: this.requireName(input.name, "state name") } : {}
|
|
21387
|
+
};
|
|
21388
|
+
if (normalizedInput.category !== void 0) this.assertCategory(normalizedInput.category);
|
|
21389
|
+
const current = await this.store.getState(projectId, stateId);
|
|
21390
|
+
if (!current) throw new ProjectWorkError("PROJECT_WORK_STATE_NOT_FOUND", "work item state was not found");
|
|
21391
|
+
if (current.isDefault && normalizedInput.isDefault === false) throw new ProjectWorkError("PROJECT_WORK_STATE_INVALID", "choose another default state before clearing this one");
|
|
21392
|
+
if (normalizedInput.name && (await this.store.listStates(projectId)).some((state) => state.id !== stateId && state.name.toLocaleLowerCase() === normalizedInput.name.toLocaleLowerCase())) throw new ProjectWorkError("PROJECT_WORK_STATE_INVALID", "state name is already in use");
|
|
21393
|
+
const state = await this.store.updateState(projectId, stateId, normalizedInput);
|
|
21394
|
+
if (!state) throw new ProjectWorkError("PROJECT_WORK_STATE_NOT_FOUND", "work item state was not found");
|
|
21395
|
+
await this.emit(projectId, "state-config");
|
|
21396
|
+
return state;
|
|
21397
|
+
};
|
|
21398
|
+
deleteState = async (projectId, stateId, migrateToStateId, actor) => {
|
|
21399
|
+
await this.ensureProject(projectId);
|
|
21400
|
+
const states = await this.store.listStates(projectId);
|
|
21401
|
+
if (states.length <= 1) throw new ProjectWorkError("PROJECT_WORK_STATE_INVALID", "a project must keep at least one state");
|
|
21402
|
+
if (migrateToStateId === stateId) throw new ProjectWorkError("PROJECT_WORK_STATE_INVALID", "migration target must be a different state");
|
|
21403
|
+
if (migrateToStateId && !states.some((state) => state.id === migrateToStateId)) throw new ProjectWorkError("PROJECT_WORK_STATE_NOT_FOUND", "migration target state was not found");
|
|
21404
|
+
try {
|
|
21405
|
+
if (!await this.store.deleteState(projectId, stateId, migrateToStateId, this.normalizeActor(actor))) throw new ProjectWorkError("PROJECT_WORK_STATE_NOT_FOUND", "work item state was not found");
|
|
21406
|
+
} catch (error) {
|
|
21407
|
+
if (error instanceof Error && error.message === "PROJECT_WORK_STATE_IN_USE") throw new ProjectWorkError("PROJECT_WORK_STATE_IN_USE", "state is used by work items; choose a migration target");
|
|
21408
|
+
throw error;
|
|
21409
|
+
}
|
|
21410
|
+
await this.emit(projectId, "state-config");
|
|
21411
|
+
};
|
|
21412
|
+
linkArtifact = async (params) => {
|
|
21413
|
+
const { actor, label, path, projectId, workItemId } = params;
|
|
21414
|
+
const project = await this.requireProject(projectId);
|
|
21415
|
+
await this.get(projectId, workItemId);
|
|
21416
|
+
const artifactPath = await this.normalizeArtifactPath(project.rootPath, path);
|
|
21417
|
+
const existing = (await this.store.listArtifacts(projectId, workItemId)).find((link) => link.path === artifactPath);
|
|
21418
|
+
if (existing) return existing;
|
|
21419
|
+
const link = await this.store.linkArtifact({
|
|
21420
|
+
projectId,
|
|
21421
|
+
workItemId,
|
|
21422
|
+
path: artifactPath,
|
|
21423
|
+
label: label?.trim() || void 0,
|
|
21424
|
+
actor: this.normalizeActor(actor)
|
|
21425
|
+
});
|
|
21426
|
+
await this.emit(projectId, "artifact", workItemId);
|
|
21427
|
+
return link;
|
|
21428
|
+
};
|
|
21429
|
+
unlinkArtifact = async (params) => {
|
|
21430
|
+
const { actor, artifactLinkId, projectId, workItemId } = params;
|
|
21431
|
+
await this.get(projectId, workItemId);
|
|
21432
|
+
if (!await this.store.unlinkArtifact({
|
|
21433
|
+
projectId,
|
|
21434
|
+
workItemId,
|
|
21435
|
+
artifactLinkId,
|
|
21436
|
+
actor: this.normalizeActor(actor)
|
|
21437
|
+
})) throw new ProjectWorkError("PROJECT_WORK_ARTIFACT_NOT_FOUND", "artifact link was not found");
|
|
21438
|
+
await this.emit(projectId, "artifact", workItemId);
|
|
21439
|
+
};
|
|
21440
|
+
setDeleted = async (projectId, workItemId, deleted, actor) => {
|
|
21441
|
+
await this.ensureProject(projectId);
|
|
21442
|
+
const current = await this.get(projectId, workItemId);
|
|
21443
|
+
if (current.deletedAt !== null === deleted) return current;
|
|
21444
|
+
if (!await this.store.setDeleted({
|
|
21445
|
+
projectId,
|
|
21446
|
+
workItemId,
|
|
21447
|
+
deleted,
|
|
21448
|
+
actor: this.normalizeActor(actor)
|
|
21449
|
+
})) throw new ProjectWorkError("PROJECT_WORK_ITEM_NOT_FOUND", "work item was not found");
|
|
21450
|
+
await this.emit(projectId, deleted ? "deleted" : "restored", workItemId);
|
|
21451
|
+
return await this.get(projectId, workItemId);
|
|
21452
|
+
};
|
|
21453
|
+
requireProject = async (projectId) => {
|
|
21454
|
+
const normalized = projectId.trim();
|
|
21455
|
+
const project = normalized ? await this.options.projectManager.getProjectById(normalized) : null;
|
|
21456
|
+
if (!project) throw new ProjectWorkError("PROJECT_NOT_FOUND", "project was not found");
|
|
21457
|
+
return project;
|
|
21458
|
+
};
|
|
21459
|
+
requireMappedState = (states, stateId) => {
|
|
21460
|
+
const state = states.get(stateId);
|
|
21461
|
+
if (!state) throw new ProjectWorkError("PROJECT_WORK_STATE_NOT_FOUND", "work item state was not found");
|
|
21462
|
+
return state;
|
|
21463
|
+
};
|
|
21464
|
+
requireName = (value, label) => {
|
|
21465
|
+
const normalized = typeof value === "string" ? value.trim() : "";
|
|
21466
|
+
if (!normalized) throw new ProjectWorkError("PROJECT_WORK_VALIDATION_FAILED", `${label} is required`);
|
|
21467
|
+
if (normalized.length > 200) throw new ProjectWorkError("PROJECT_WORK_VALIDATION_FAILED", `${label} is too long`);
|
|
21468
|
+
return normalized;
|
|
21469
|
+
};
|
|
21470
|
+
assertAttention = (value) => {
|
|
21471
|
+
if (!PROJECT_WORK_ATTENTION_VALUES.some((entry) => entry === value)) throw new ProjectWorkError("PROJECT_WORK_VALIDATION_FAILED", "work item attention is invalid");
|
|
21472
|
+
};
|
|
21473
|
+
assertCategory = (value) => {
|
|
21474
|
+
if (!PROJECT_WORK_STATE_CATEGORIES.some((entry) => entry === value)) throw new ProjectWorkError("PROJECT_WORK_VALIDATION_FAILED", "work item state category is invalid");
|
|
21475
|
+
};
|
|
21476
|
+
normalizeActor = (actor) => ({
|
|
21477
|
+
kind: actor.kind,
|
|
21478
|
+
...actor.id?.trim() ? { id: actor.id.trim() } : {},
|
|
21479
|
+
...actor.sessionId?.trim() ? { sessionId: actor.sessionId.trim() } : {}
|
|
21480
|
+
});
|
|
21481
|
+
normalizeArtifactPath = async (rootPath, inputPath) => {
|
|
21482
|
+
const requested = inputPath.trim();
|
|
21483
|
+
if (!requested) throw new ProjectWorkError("PROJECT_WORK_ARTIFACT_INVALID", "artifact path is required");
|
|
21484
|
+
const canonicalRoot = await realpath(rootPath);
|
|
21485
|
+
const candidate = await realpath(isAbsolute(requested) ? requested : resolve(canonicalRoot, requested)).catch(() => null);
|
|
21486
|
+
if (!candidate) throw new ProjectWorkError("PROJECT_WORK_ARTIFACT_NOT_FOUND", "artifact file does not exist");
|
|
21487
|
+
const relativePath = relative(canonicalRoot, candidate);
|
|
21488
|
+
if (!relativePath || relativePath === ".." || relativePath.startsWith(`..${sep}`) || isAbsolute(relativePath)) throw new ProjectWorkError("PROJECT_WORK_ARTIFACT_INVALID", "artifact must be a file inside the project root");
|
|
21489
|
+
if (!(await stat(candidate)).isFile()) throw new ProjectWorkError("PROJECT_WORK_ARTIFACT_INVALID", "artifact must be a file");
|
|
21490
|
+
return relativePath.split(sep).join("/");
|
|
21491
|
+
};
|
|
21492
|
+
emit = async (projectId, change, workItemId) => {
|
|
21493
|
+
await this.options.eventBus.emit(eventKeys$1.projectWorkChanged, {
|
|
21494
|
+
projectId,
|
|
21495
|
+
change,
|
|
21496
|
+
...workItemId ? { workItemId } : {}
|
|
21497
|
+
});
|
|
21498
|
+
};
|
|
21499
|
+
};
|
|
21500
|
+
//#endregion
|
|
21501
|
+
//#region src/features/projects/types/project-observation.types.ts
|
|
21502
|
+
const PROJECT_OBSERVATION_PROTOCOL = "nextclaw.project/v1";
|
|
21503
|
+
var ProjectObservationError = class extends Error {
|
|
21504
|
+
constructor(code, message) {
|
|
21505
|
+
super(message);
|
|
21506
|
+
this.code = code;
|
|
21507
|
+
this.name = "ProjectObservationError";
|
|
21508
|
+
}
|
|
21509
|
+
};
|
|
21510
|
+
function isProjectObservationError(error) {
|
|
21511
|
+
return error instanceof ProjectObservationError;
|
|
21512
|
+
}
|
|
21513
|
+
//#endregion
|
|
21514
|
+
//#region src/features/projects/utils/project-observation-config.utils.ts
|
|
21515
|
+
const isRecord$8 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21516
|
+
const readString$7 = (value) => {
|
|
21517
|
+
if (typeof value !== "string") return null;
|
|
21518
|
+
return value.trim() || null;
|
|
21519
|
+
};
|
|
21520
|
+
const collectUnknownKeyIssues = (value, allowed, owner) => Object.keys(value).flatMap((key) => allowed.includes(key) ? [] : [{
|
|
21521
|
+
code: "PROJECT_CONFIG_UNKNOWN_FIELD",
|
|
21522
|
+
message: `${owner} contains unknown field '${key}'.`
|
|
21523
|
+
}]);
|
|
21524
|
+
function parseContext(value) {
|
|
21525
|
+
if (value === void 0) return {
|
|
21526
|
+
value: [],
|
|
21527
|
+
issues: []
|
|
21528
|
+
};
|
|
21529
|
+
if (!Array.isArray(value)) return {
|
|
21530
|
+
value: [],
|
|
21531
|
+
issues: [{
|
|
21532
|
+
code: "PROJECT_CONFIG_CONTEXT_INVALID",
|
|
21533
|
+
message: "project.context must be an array."
|
|
21534
|
+
}]
|
|
21535
|
+
};
|
|
21536
|
+
const issues = [];
|
|
21537
|
+
return {
|
|
21538
|
+
value: value.flatMap((entry, index) => {
|
|
21539
|
+
if (!isRecord$8(entry)) {
|
|
21540
|
+
issues.push({
|
|
21541
|
+
code: "PROJECT_CONFIG_CONTEXT_INVALID",
|
|
21542
|
+
message: `project.context[${index}] must be an object.`
|
|
21543
|
+
});
|
|
21544
|
+
return [];
|
|
21545
|
+
}
|
|
21546
|
+
issues.push(...collectUnknownKeyIssues(entry, [
|
|
21547
|
+
"id",
|
|
21548
|
+
"role",
|
|
21549
|
+
"source"
|
|
21550
|
+
], `project.context[${index}]`));
|
|
21551
|
+
const id = readString$7(entry.id);
|
|
21552
|
+
const role = readString$7(entry.role);
|
|
21553
|
+
const source = readString$7(entry.source);
|
|
21554
|
+
if (!id || !role || !source) {
|
|
21555
|
+
issues.push({
|
|
21556
|
+
code: "PROJECT_CONFIG_CONTEXT_INVALID",
|
|
21557
|
+
message: `project.context[${index}] requires id, role and source.`
|
|
21558
|
+
});
|
|
21559
|
+
return [];
|
|
21560
|
+
}
|
|
21561
|
+
return [{
|
|
21562
|
+
id,
|
|
21563
|
+
role,
|
|
21564
|
+
source
|
|
21565
|
+
}];
|
|
21566
|
+
}),
|
|
21567
|
+
issues
|
|
21568
|
+
};
|
|
21569
|
+
}
|
|
21570
|
+
function parseWorkflows(value) {
|
|
21571
|
+
if (value === void 0) return {
|
|
21572
|
+
value: [],
|
|
21573
|
+
issues: []
|
|
21574
|
+
};
|
|
21575
|
+
if (!Array.isArray(value)) return {
|
|
20556
21576
|
value: [],
|
|
20557
21577
|
issues: [{
|
|
20558
21578
|
code: "PROJECT_CONFIG_WORKFLOWS_INVALID",
|
|
@@ -20562,7 +21582,7 @@ function parseWorkflows(value) {
|
|
|
20562
21582
|
const issues = [];
|
|
20563
21583
|
return {
|
|
20564
21584
|
value: value.flatMap((entry, workflowIndex) => {
|
|
20565
|
-
if (!isRecord$
|
|
21585
|
+
if (!isRecord$8(entry) || !Array.isArray(entry.stages)) {
|
|
20566
21586
|
issues.push({
|
|
20567
21587
|
code: "PROJECT_CONFIG_WORKFLOW_INVALID",
|
|
20568
21588
|
message: `workflows[${workflowIndex}] requires a stages array.`
|
|
@@ -20577,7 +21597,7 @@ function parseWorkflows(value) {
|
|
|
20577
21597
|
const id = readString$7(entry.id);
|
|
20578
21598
|
const label = readString$7(entry.label);
|
|
20579
21599
|
const stages = entry.stages.flatMap((stage, stageIndex) => {
|
|
20580
|
-
if (!isRecord$
|
|
21600
|
+
if (!isRecord$8(stage)) {
|
|
20581
21601
|
issues.push({
|
|
20582
21602
|
code: "PROJECT_CONFIG_STAGE_INVALID",
|
|
20583
21603
|
message: `workflows[${workflowIndex}].stages[${stageIndex}] must be an object.`
|
|
@@ -20630,7 +21650,7 @@ function parseArtifactCategories(value) {
|
|
|
20630
21650
|
const issues = [];
|
|
20631
21651
|
return {
|
|
20632
21652
|
value: value.flatMap((entry, index) => {
|
|
20633
|
-
if (!isRecord$
|
|
21653
|
+
if (!isRecord$8(entry) || !Array.isArray(entry.include)) {
|
|
20634
21654
|
issues.push({
|
|
20635
21655
|
code: "PROJECT_CONFIG_ARTIFACT_INVALID",
|
|
20636
21656
|
message: `observation.artifacts[${index}] requires an include array.`
|
|
@@ -20676,7 +21696,7 @@ function parseSkillRoots(value) {
|
|
|
20676
21696
|
const issues = [];
|
|
20677
21697
|
return {
|
|
20678
21698
|
value: value.flatMap((entry, index) => {
|
|
20679
|
-
if (!isRecord$
|
|
21699
|
+
if (!isRecord$8(entry)) {
|
|
20680
21700
|
issues.push({
|
|
20681
21701
|
code: "PROJECT_CONFIG_SKILL_INVALID",
|
|
20682
21702
|
message: `observation.skills[${index}] must be an object.`
|
|
@@ -20711,7 +21731,7 @@ function parseProjectObservationConfig(source) {
|
|
|
20711
21731
|
}]
|
|
20712
21732
|
};
|
|
20713
21733
|
}
|
|
20714
|
-
if (!isRecord$
|
|
21734
|
+
if (!isRecord$8(parsed)) return {
|
|
20715
21735
|
config: null,
|
|
20716
21736
|
issues: [{
|
|
20717
21737
|
code: "PROJECT_CONFIG_INVALID",
|
|
@@ -20731,13 +21751,13 @@ function parseProjectObservationConfig(source) {
|
|
|
20731
21751
|
message: "Only schema_version 1 is supported."
|
|
20732
21752
|
}, ...issues]
|
|
20733
21753
|
};
|
|
20734
|
-
const project = isRecord$
|
|
20735
|
-
const observation = isRecord$
|
|
20736
|
-
if (parsed.project !== void 0 && !isRecord$
|
|
21754
|
+
const project = isRecord$8(parsed.project) ? parsed.project : {};
|
|
21755
|
+
const observation = isRecord$8(parsed.observation) ? parsed.observation : {};
|
|
21756
|
+
if (parsed.project !== void 0 && !isRecord$8(parsed.project)) issues.push({
|
|
20737
21757
|
code: "PROJECT_CONFIG_PROJECT_INVALID",
|
|
20738
21758
|
message: "project must be an object."
|
|
20739
21759
|
});
|
|
20740
|
-
if (parsed.observation !== void 0 && !isRecord$
|
|
21760
|
+
if (parsed.observation !== void 0 && !isRecord$8(parsed.observation)) issues.push({
|
|
20741
21761
|
code: "PROJECT_CONFIG_OBSERVATION_INVALID",
|
|
20742
21762
|
message: "observation must be an object."
|
|
20743
21763
|
});
|
|
@@ -20749,7 +21769,7 @@ function parseProjectObservationConfig(source) {
|
|
|
20749
21769
|
"skills"
|
|
20750
21770
|
], "observation"));
|
|
20751
21771
|
if (observation.markers !== void 0) {
|
|
20752
|
-
if (!(Array.isArray(observation.markers) ? observation.markers : []).some((marker) => isRecord$
|
|
21772
|
+
if (!(Array.isArray(observation.markers) ? observation.markers : []).some((marker) => isRecord$8(marker) && marker.protocol === "nextclaw.project/v1")) issues.push({
|
|
20753
21773
|
code: "PROJECT_CONFIG_MARKERS_INVALID",
|
|
20754
21774
|
message: `observation.markers must enable protocol '${PROJECT_OBSERVATION_PROTOCOL}'.`
|
|
20755
21775
|
});
|
|
@@ -21402,10 +22422,10 @@ function parseProjectObservationMarkers(params) {
|
|
|
21402
22422
|
issues
|
|
21403
22423
|
};
|
|
21404
22424
|
}
|
|
21405
|
-
const isRecord$
|
|
22425
|
+
const isRecord$7 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21406
22426
|
function readProjectObservationResponseMetadata(metadata) {
|
|
21407
22427
|
const response = metadata?.project_observation_response;
|
|
21408
|
-
if (!isRecord$
|
|
22428
|
+
if (!isRecord$7(response) || response.protocol !== "nextclaw.project/v1" || typeof response.requestId !== "string" || response.decision !== "confirmed" && response.decision !== "rejected") return null;
|
|
21409
22429
|
return {
|
|
21410
22430
|
protocol: PROJECT_OBSERVATION_PROTOCOL,
|
|
21411
22431
|
requestId: response.requestId,
|
|
@@ -22046,7 +23066,7 @@ function deduplicateNcpAgentSessionTailMessages(messages) {
|
|
|
22046
23066
|
function isNcpAgentSessionMessageProjectionMeta(value, sessionId) {
|
|
22047
23067
|
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
22048
23068
|
const meta = value;
|
|
22049
|
-
return meta.version === 7 && meta.sessionId === sessionId && Number.isSafeInteger(meta.total) && Number.isSafeInteger(meta.projectedJournalOffset) && Number.isSafeInteger(meta.dataBytes) && (meta.activeMessageId === null || typeof meta.activeMessageId === "string") && Array.isArray(meta.pendingCompactionMessageIds) && meta.pendingCompactionMessageIds.every((id) => typeof id === "string") && (meta.contextWindow === null || isRecord$
|
|
23069
|
+
return meta.version === 7 && meta.sessionId === sessionId && Number.isSafeInteger(meta.total) && Number.isSafeInteger(meta.projectedJournalOffset) && Number.isSafeInteger(meta.dataBytes) && (meta.activeMessageId === null || typeof meta.activeMessageId === "string") && Array.isArray(meta.pendingCompactionMessageIds) && meta.pendingCompactionMessageIds.every((id) => typeof id === "string") && (meta.contextWindow === null || isRecord$6(meta.contextWindow));
|
|
22050
23070
|
}
|
|
22051
23071
|
function readActiveAssistantMessageId(messages) {
|
|
22052
23072
|
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
@@ -22069,12 +23089,12 @@ function mergePendingCompactionMessageIds(current, messages) {
|
|
|
22069
23089
|
}
|
|
22070
23090
|
return pending;
|
|
22071
23091
|
}
|
|
22072
|
-
function isRecord$
|
|
23092
|
+
function isRecord$6(value) {
|
|
22073
23093
|
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
22074
23094
|
}
|
|
22075
23095
|
function readCompactionStatus(message) {
|
|
22076
23096
|
const checkpoint = message.metadata?.checkpoint;
|
|
22077
|
-
if (message.metadata?.nextclaw_timeline_kind !== "context_compaction" || !isRecord$
|
|
23097
|
+
if (message.metadata?.nextclaw_timeline_kind !== "context_compaction" || !isRecord$6(checkpoint)) return null;
|
|
22078
23098
|
return typeof checkpoint.status === "string" ? checkpoint.status : null;
|
|
22079
23099
|
}
|
|
22080
23100
|
//#endregion
|
|
@@ -22582,151 +23602,6 @@ async function readLegacyRecords(journalDir, diagnostics) {
|
|
|
22582
23602
|
return /* @__PURE__ */ new Map();
|
|
22583
23603
|
}
|
|
22584
23604
|
//#endregion
|
|
22585
|
-
//#region src/stores/ncp-session-catalog-sqlite-driver.store.ts
|
|
22586
|
-
const require = createRequire(import.meta.url);
|
|
22587
|
-
const portableDatabases = /* @__PURE__ */ new Map();
|
|
22588
|
-
async function openSessionCatalogSqliteDatabase(databasePath) {
|
|
22589
|
-
try {
|
|
22590
|
-
return new (require("node:sqlite")).DatabaseSync(databasePath);
|
|
22591
|
-
} catch (error) {
|
|
22592
|
-
if (!isMissingNodeSqlite(error)) throw error;
|
|
22593
|
-
return await openPortableDatabase(databasePath);
|
|
22594
|
-
}
|
|
22595
|
-
}
|
|
22596
|
-
function runSessionCatalogSqliteTransaction(database, operation, mode = "DEFERRED") {
|
|
22597
|
-
database.exec(`BEGIN ${mode}`);
|
|
22598
|
-
try {
|
|
22599
|
-
const result = operation();
|
|
22600
|
-
database.exec("COMMIT");
|
|
22601
|
-
return result;
|
|
22602
|
-
} catch (error) {
|
|
22603
|
-
database.exec("ROLLBACK");
|
|
22604
|
-
throw error;
|
|
22605
|
-
}
|
|
22606
|
-
}
|
|
22607
|
-
function isMissingNodeSqlite(error) {
|
|
22608
|
-
const code = typeof error === "object" && error !== null ? error.code : void 0;
|
|
22609
|
-
return code === "ERR_UNKNOWN_BUILTIN_MODULE" || code === "MODULE_NOT_FOUND";
|
|
22610
|
-
}
|
|
22611
|
-
async function openPortableDatabase(databasePath) {
|
|
22612
|
-
const existing = portableDatabases.get(databasePath);
|
|
22613
|
-
if (existing) return (await existing).acquire();
|
|
22614
|
-
const opening = createPortableDatabase(databasePath).catch((error) => {
|
|
22615
|
-
portableDatabases.delete(databasePath);
|
|
22616
|
-
throw error;
|
|
22617
|
-
});
|
|
22618
|
-
portableDatabases.set(databasePath, opening);
|
|
22619
|
-
return (await opening).acquire();
|
|
22620
|
-
}
|
|
22621
|
-
async function createPortableDatabase(databasePath) {
|
|
22622
|
-
const initialize = (await import("sql.js")).default;
|
|
22623
|
-
const sqlite = await initialize({ locateFile: (file) => require.resolve(`sql.js/dist/${file}`) });
|
|
22624
|
-
return new PortableSessionCatalogSqliteDatabase(databasePath, existsSync(databasePath) ? new sqlite.Database(readFileSync(databasePath)) : new sqlite.Database());
|
|
22625
|
-
}
|
|
22626
|
-
var PortableSessionCatalogSqliteDatabase = class {
|
|
22627
|
-
inTransaction = false;
|
|
22628
|
-
dirty = false;
|
|
22629
|
-
leaseCount = 0;
|
|
22630
|
-
constructor(databasePath, database) {
|
|
22631
|
-
this.databasePath = databasePath;
|
|
22632
|
-
this.database = database;
|
|
22633
|
-
}
|
|
22634
|
-
acquire = () => {
|
|
22635
|
-
this.leaseCount += 1;
|
|
22636
|
-
return new PortableSessionCatalogSqliteLease(this);
|
|
22637
|
-
};
|
|
22638
|
-
exec = (sql) => {
|
|
22639
|
-
const command = sql.trim().toUpperCase();
|
|
22640
|
-
this.database.run(sql);
|
|
22641
|
-
if (command.startsWith("BEGIN")) {
|
|
22642
|
-
this.inTransaction = true;
|
|
22643
|
-
return;
|
|
22644
|
-
}
|
|
22645
|
-
if (command.startsWith("ROLLBACK")) {
|
|
22646
|
-
this.inTransaction = false;
|
|
22647
|
-
this.dirty = false;
|
|
22648
|
-
return;
|
|
22649
|
-
}
|
|
22650
|
-
if (command.startsWith("COMMIT")) {
|
|
22651
|
-
this.inTransaction = false;
|
|
22652
|
-
this.persistIfDirty();
|
|
22653
|
-
return;
|
|
22654
|
-
}
|
|
22655
|
-
if (!command.startsWith("PRAGMA")) this.markDirty();
|
|
22656
|
-
};
|
|
22657
|
-
prepare = (sql) => new PortableSessionCatalogSqliteStatement(this, sql);
|
|
22658
|
-
release = () => {
|
|
22659
|
-
this.leaseCount -= 1;
|
|
22660
|
-
if (this.leaseCount > 0) return;
|
|
22661
|
-
this.persistIfDirty();
|
|
22662
|
-
this.database.close();
|
|
22663
|
-
portableDatabases.delete(this.databasePath);
|
|
22664
|
-
};
|
|
22665
|
-
run = (sql, params) => {
|
|
22666
|
-
this.database.run(sql, normalizeParams(params));
|
|
22667
|
-
this.markDirty();
|
|
22668
|
-
};
|
|
22669
|
-
readRows = (sql, params) => {
|
|
22670
|
-
const statement = this.database.prepare(sql);
|
|
22671
|
-
try {
|
|
22672
|
-
bindStatement(statement, params);
|
|
22673
|
-
const rows = [];
|
|
22674
|
-
while (statement.step()) rows.push(statement.getAsObject());
|
|
22675
|
-
return rows;
|
|
22676
|
-
} finally {
|
|
22677
|
-
statement.free();
|
|
22678
|
-
}
|
|
22679
|
-
};
|
|
22680
|
-
markDirty = () => {
|
|
22681
|
-
this.dirty = true;
|
|
22682
|
-
if (!this.inTransaction) this.persistIfDirty();
|
|
22683
|
-
};
|
|
22684
|
-
persistIfDirty = () => {
|
|
22685
|
-
if (!this.dirty) return;
|
|
22686
|
-
const temporaryPath = `${this.databasePath}.tmp`;
|
|
22687
|
-
writeFileSync(temporaryPath, this.database.export());
|
|
22688
|
-
renameSync(temporaryPath, this.databasePath);
|
|
22689
|
-
this.dirty = false;
|
|
22690
|
-
};
|
|
22691
|
-
};
|
|
22692
|
-
var PortableSessionCatalogSqliteLease = class {
|
|
22693
|
-
closed = false;
|
|
22694
|
-
constructor(owner) {
|
|
22695
|
-
this.owner = owner;
|
|
22696
|
-
}
|
|
22697
|
-
close = () => {
|
|
22698
|
-
if (this.closed) return;
|
|
22699
|
-
this.closed = true;
|
|
22700
|
-
this.owner.release();
|
|
22701
|
-
};
|
|
22702
|
-
exec = (sql) => this.owner.exec(sql);
|
|
22703
|
-
prepare = (sql) => this.owner.prepare(sql);
|
|
22704
|
-
};
|
|
22705
|
-
var PortableSessionCatalogSqliteStatement = class {
|
|
22706
|
-
constructor(database, sql) {
|
|
22707
|
-
this.database = database;
|
|
22708
|
-
this.sql = sql;
|
|
22709
|
-
}
|
|
22710
|
-
all = (...params) => this.database.readRows(this.sql, params);
|
|
22711
|
-
get = (...params) => this.all(...params)[0];
|
|
22712
|
-
run = (...params) => {
|
|
22713
|
-
this.database.run(this.sql, params);
|
|
22714
|
-
return {};
|
|
22715
|
-
};
|
|
22716
|
-
};
|
|
22717
|
-
function bindStatement(statement, params) {
|
|
22718
|
-
const normalized = normalizeParams(params);
|
|
22719
|
-
if (Array.isArray(normalized) && normalized.length === 0) return;
|
|
22720
|
-
statement.bind(normalized);
|
|
22721
|
-
}
|
|
22722
|
-
function normalizeParams(params) {
|
|
22723
|
-
if (params.length !== 1 || !isRecord$6(params[0])) return params;
|
|
22724
|
-
return Object.fromEntries(Object.entries(params[0]).map(([key, value]) => [`@${key}`, value]));
|
|
22725
|
-
}
|
|
22726
|
-
function isRecord$6(value) {
|
|
22727
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
22728
|
-
}
|
|
22729
|
-
//#endregion
|
|
22730
23605
|
//#region src/stores/ncp-agent-session-summary-index.store.ts
|
|
22731
23606
|
const SQLITE_DATABASE_FILE = ".ncp-agent-session-catalog.sqlite";
|
|
22732
23607
|
const CATALOG_SCHEMA_VERSION = 1;
|
|
@@ -22866,7 +23741,7 @@ var NcpAgentSessionSummaryIndexStore = class {
|
|
|
22866
23741
|
await this.ensureReady();
|
|
22867
23742
|
const normalizedSessionId = normalizeNcpSessionId(sessionId);
|
|
22868
23743
|
const deletedAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
22869
|
-
|
|
23744
|
+
runSqliteTransaction(this.db(), () => {
|
|
22870
23745
|
if (this.db().prepare("SELECT session_id FROM sessions WHERE session_id = ? LIMIT 1").get(normalizedSessionId)) {
|
|
22871
23746
|
this.db().prepare("UPDATE sessions SET deleted_at = ? WHERE session_id = ?").run(deletedAt, normalizedSessionId);
|
|
22872
23747
|
return;
|
|
@@ -22891,7 +23766,7 @@ var NcpAgentSessionSummaryIndexStore = class {
|
|
|
22891
23766
|
};
|
|
22892
23767
|
initializeCatalog = async () => {
|
|
22893
23768
|
await mkdir(this.journalDir, { recursive: true });
|
|
22894
|
-
this.database = await
|
|
23769
|
+
this.database = await openSqliteDatabase(resolve(this.journalDir, SQLITE_DATABASE_FILE));
|
|
22895
23770
|
this.database.exec(`
|
|
22896
23771
|
PRAGMA busy_timeout = 10000;
|
|
22897
23772
|
PRAGMA journal_mode = WAL;
|
|
@@ -22931,7 +23806,7 @@ var NcpAgentSessionSummaryIndexStore = class {
|
|
|
22931
23806
|
loadSession: (sessionId) => this.loadSession(sessionId),
|
|
22932
23807
|
loadSessionSummary: this.loadSessionSummary
|
|
22933
23808
|
});
|
|
22934
|
-
|
|
23809
|
+
runSqliteTransaction(this.db(), () => {
|
|
22935
23810
|
if (this.db().prepare("SELECT value FROM storage_meta WHERE key = ? LIMIT 1").get(MIGRATION_STATUS_KEY)?.value === MIGRATION_COMPLETE) {
|
|
22936
23811
|
this.reconcileRecords(scan.records);
|
|
22937
23812
|
return;
|
|
@@ -25786,12 +26661,234 @@ var ProjectsCreateTool = class {
|
|
|
25786
26661
|
};
|
|
25787
26662
|
};
|
|
25788
26663
|
//#endregion
|
|
26664
|
+
//#region src/tools/project-work.tools.ts
|
|
26665
|
+
function requiredString$1(value, key) {
|
|
26666
|
+
if (typeof value !== "string" || !value.trim()) throw new Error(`${key} must be a non-empty string.`);
|
|
26667
|
+
return value.trim();
|
|
26668
|
+
}
|
|
26669
|
+
function optionalString$1(value) {
|
|
26670
|
+
return typeof value === "string" && value.trim() ? value.trim() : void 0;
|
|
26671
|
+
}
|
|
26672
|
+
function optionalBoolean(value) {
|
|
26673
|
+
return typeof value === "boolean" ? value : void 0;
|
|
26674
|
+
}
|
|
26675
|
+
function optionalNumber(value) {
|
|
26676
|
+
return typeof value === "number" && Number.isInteger(value) ? value : void 0;
|
|
26677
|
+
}
|
|
26678
|
+
function agentActor(context) {
|
|
26679
|
+
return {
|
|
26680
|
+
kind: "agent",
|
|
26681
|
+
...context.agentId ? { id: context.agentId } : {},
|
|
26682
|
+
sessionId: context.sessionId
|
|
26683
|
+
};
|
|
26684
|
+
}
|
|
26685
|
+
var ProjectWorkListTool = class {
|
|
26686
|
+
name = "project_work_list";
|
|
26687
|
+
description = "List persistent work items and custom states for the current project.";
|
|
26688
|
+
parameters = {
|
|
26689
|
+
type: "object",
|
|
26690
|
+
properties: { include_deleted: {
|
|
26691
|
+
type: "boolean",
|
|
26692
|
+
description: "Include deleted work items."
|
|
26693
|
+
} },
|
|
26694
|
+
additionalProperties: false
|
|
26695
|
+
};
|
|
26696
|
+
constructor(work, context) {
|
|
26697
|
+
this.work = work;
|
|
26698
|
+
this.context = context;
|
|
26699
|
+
}
|
|
26700
|
+
execute = async (args) => {
|
|
26701
|
+
const params = normalizeToolParams(args);
|
|
26702
|
+
return JSON.stringify(await this.work.list(this.context.projectId, optionalBoolean(params.include_deleted) ?? false), null, 2);
|
|
26703
|
+
};
|
|
26704
|
+
};
|
|
26705
|
+
var ProjectWorkGetTool = class {
|
|
26706
|
+
name = "project_work_get";
|
|
26707
|
+
description = "Get one current-project work item with artifact links and recent immutable activity.";
|
|
26708
|
+
parameters = {
|
|
26709
|
+
type: "object",
|
|
26710
|
+
properties: {
|
|
26711
|
+
id: {
|
|
26712
|
+
type: "string",
|
|
26713
|
+
description: "Work item id."
|
|
26714
|
+
},
|
|
26715
|
+
activity_limit: {
|
|
26716
|
+
type: "integer",
|
|
26717
|
+
minimum: 1,
|
|
26718
|
+
maximum: 100,
|
|
26719
|
+
description: "Recent activity entries. Defaults to 20."
|
|
26720
|
+
}
|
|
26721
|
+
},
|
|
26722
|
+
required: ["id"],
|
|
26723
|
+
additionalProperties: false
|
|
26724
|
+
};
|
|
26725
|
+
constructor(work, context) {
|
|
26726
|
+
this.work = work;
|
|
26727
|
+
this.context = context;
|
|
26728
|
+
}
|
|
26729
|
+
execute = async (args) => {
|
|
26730
|
+
const params = normalizeToolParams(args);
|
|
26731
|
+
const id = requiredString$1(params.id, "id");
|
|
26732
|
+
const [item, activity] = await Promise.all([this.work.get(this.context.projectId, id), this.work.listActivities(this.context.projectId, id, { limit: optionalNumber(params.activity_limit) ?? 20 })]);
|
|
26733
|
+
return JSON.stringify({
|
|
26734
|
+
item,
|
|
26735
|
+
activity
|
|
26736
|
+
}, null, 2);
|
|
26737
|
+
};
|
|
26738
|
+
};
|
|
26739
|
+
var ProjectWorkCreateTool = class {
|
|
26740
|
+
name = "project_work_create";
|
|
26741
|
+
description = "Create a persistent work item in the current project.";
|
|
26742
|
+
parameters = {
|
|
26743
|
+
type: "object",
|
|
26744
|
+
properties: {
|
|
26745
|
+
title: {
|
|
26746
|
+
type: "string",
|
|
26747
|
+
description: "Concise work item title."
|
|
26748
|
+
},
|
|
26749
|
+
description: {
|
|
26750
|
+
type: "string",
|
|
26751
|
+
description: "Optional durable context and acceptance notes."
|
|
26752
|
+
},
|
|
26753
|
+
state_id: {
|
|
26754
|
+
type: "string",
|
|
26755
|
+
description: "Optional custom state id. The project default is used when omitted."
|
|
26756
|
+
},
|
|
26757
|
+
attention: {
|
|
26758
|
+
type: "string",
|
|
26759
|
+
enum: [
|
|
26760
|
+
"none",
|
|
26761
|
+
"blocked",
|
|
26762
|
+
"awaiting-user"
|
|
26763
|
+
]
|
|
26764
|
+
}
|
|
26765
|
+
},
|
|
26766
|
+
required: ["title"],
|
|
26767
|
+
additionalProperties: false
|
|
26768
|
+
};
|
|
26769
|
+
constructor(work, context) {
|
|
26770
|
+
this.work = work;
|
|
26771
|
+
this.context = context;
|
|
26772
|
+
}
|
|
26773
|
+
execute = async (args) => {
|
|
26774
|
+
const params = normalizeToolParams(args);
|
|
26775
|
+
return JSON.stringify(await this.work.create(this.context.projectId, {
|
|
26776
|
+
title: requiredString$1(params.title, "title"),
|
|
26777
|
+
...optionalString$1(params.description) ? { description: optionalString$1(params.description) } : {},
|
|
26778
|
+
...optionalString$1(params.state_id) ? { stateId: optionalString$1(params.state_id) } : {},
|
|
26779
|
+
...optionalString$1(params.attention) ? { attention: optionalString$1(params.attention) } : {}
|
|
26780
|
+
}, agentActor(this.context)), null, 2);
|
|
26781
|
+
};
|
|
26782
|
+
};
|
|
26783
|
+
var ProjectWorkUpdateTool = class {
|
|
26784
|
+
name = "project_work_update";
|
|
26785
|
+
description = "Update, soft-delete, or restore a current-project work item, or add/remove its artifact links. Status changes use state_id; there is no separate start tool.";
|
|
26786
|
+
parameters = {
|
|
26787
|
+
type: "object",
|
|
26788
|
+
properties: {
|
|
26789
|
+
id: {
|
|
26790
|
+
type: "string",
|
|
26791
|
+
description: "Work item id."
|
|
26792
|
+
},
|
|
26793
|
+
title: { type: "string" },
|
|
26794
|
+
description: { type: "string" },
|
|
26795
|
+
state_id: { type: "string" },
|
|
26796
|
+
attention: {
|
|
26797
|
+
type: "string",
|
|
26798
|
+
enum: [
|
|
26799
|
+
"none",
|
|
26800
|
+
"blocked",
|
|
26801
|
+
"awaiting-user"
|
|
26802
|
+
]
|
|
26803
|
+
},
|
|
26804
|
+
expected_version: {
|
|
26805
|
+
type: "integer",
|
|
26806
|
+
minimum: 1
|
|
26807
|
+
},
|
|
26808
|
+
deleted: {
|
|
26809
|
+
type: "boolean",
|
|
26810
|
+
description: "True to soft-delete the item; false to restore it."
|
|
26811
|
+
},
|
|
26812
|
+
add_artifact: {
|
|
26813
|
+
type: "object",
|
|
26814
|
+
properties: {
|
|
26815
|
+
path: { type: "string" },
|
|
26816
|
+
label: { type: "string" }
|
|
26817
|
+
},
|
|
26818
|
+
required: ["path"],
|
|
26819
|
+
additionalProperties: false
|
|
26820
|
+
},
|
|
26821
|
+
remove_artifact_id: { type: "string" }
|
|
26822
|
+
},
|
|
26823
|
+
required: ["id"],
|
|
26824
|
+
additionalProperties: false
|
|
26825
|
+
};
|
|
26826
|
+
constructor(work, context) {
|
|
26827
|
+
this.work = work;
|
|
26828
|
+
this.context = context;
|
|
26829
|
+
}
|
|
26830
|
+
execute = async (args) => {
|
|
26831
|
+
const params = normalizeToolParams(args);
|
|
26832
|
+
const id = requiredString$1(params.id, "id");
|
|
26833
|
+
const actor = agentActor(this.context);
|
|
26834
|
+
const patch = {
|
|
26835
|
+
...params.title !== void 0 ? { title: requiredString$1(params.title, "title") } : {},
|
|
26836
|
+
...typeof params.description === "string" ? { description: params.description } : {},
|
|
26837
|
+
...optionalString$1(params.state_id) ? { stateId: optionalString$1(params.state_id) } : {},
|
|
26838
|
+
...optionalString$1(params.attention) ? { attention: optionalString$1(params.attention) } : {},
|
|
26839
|
+
...optionalNumber(params.expected_version) ? { expectedVersion: optionalNumber(params.expected_version) } : {}
|
|
26840
|
+
};
|
|
26841
|
+
if (Object.keys(patch).length > 0) await this.work.update(this.context.projectId, id, patch, actor);
|
|
26842
|
+
const deleted = optionalBoolean(params.deleted);
|
|
26843
|
+
if (deleted !== void 0) await (deleted ? this.work.delete(this.context.projectId, id, actor) : this.work.restore(this.context.projectId, id, actor));
|
|
26844
|
+
if (params.add_artifact && typeof params.add_artifact === "object" && !Array.isArray(params.add_artifact)) {
|
|
26845
|
+
const artifact = params.add_artifact;
|
|
26846
|
+
await this.work.linkArtifact({
|
|
26847
|
+
projectId: this.context.projectId,
|
|
26848
|
+
workItemId: id,
|
|
26849
|
+
path: requiredString$1(artifact.path, "add_artifact.path"),
|
|
26850
|
+
...optionalString$1(artifact.label) ? { label: optionalString$1(artifact.label) } : {},
|
|
26851
|
+
actor
|
|
26852
|
+
});
|
|
26853
|
+
}
|
|
26854
|
+
const removeArtifactId = optionalString$1(params.remove_artifact_id);
|
|
26855
|
+
if (removeArtifactId) await this.work.unlinkArtifact({
|
|
26856
|
+
projectId: this.context.projectId,
|
|
26857
|
+
workItemId: id,
|
|
26858
|
+
artifactLinkId: removeArtifactId,
|
|
26859
|
+
actor
|
|
26860
|
+
});
|
|
26861
|
+
return JSON.stringify(await this.work.get(this.context.projectId, id), null, 2);
|
|
26862
|
+
};
|
|
26863
|
+
};
|
|
26864
|
+
//#endregion
|
|
25789
26865
|
//#region src/contributions/tool-provider/providers/project-tool.provider.ts
|
|
25790
26866
|
var ProjectToolProvider = class {
|
|
25791
|
-
constructor(projectManager) {
|
|
26867
|
+
constructor(runContext, projectManager, projectWork) {
|
|
26868
|
+
this.runContext = runContext;
|
|
25792
26869
|
this.projectManager = projectManager;
|
|
26870
|
+
this.projectWork = projectWork;
|
|
25793
26871
|
}
|
|
25794
|
-
provide = (
|
|
26872
|
+
provide = async (request) => {
|
|
26873
|
+
const globalTools = [new ProjectsListTool(this.projectManager), new ProjectsCreateTool(this.projectManager)];
|
|
26874
|
+
const resolved = await this.runContext.resolve(request);
|
|
26875
|
+
const projectId = readProjectId(resolved.session?.metadata);
|
|
26876
|
+
const projectRoot = readProjectRoot(resolved.session?.metadata);
|
|
26877
|
+
const project = projectId ? await this.projectManager.getProjectById(projectId) : projectRoot ? await this.projectManager.getRegisteredProject(projectRoot) : null;
|
|
26878
|
+
if (!project || !resolved.sessionId) return globalTools;
|
|
26879
|
+
const context = {
|
|
26880
|
+
projectId: project.id,
|
|
26881
|
+
sessionId: resolved.sessionId,
|
|
26882
|
+
...resolved.session?.agentId ? { agentId: resolved.session.agentId } : {}
|
|
26883
|
+
};
|
|
26884
|
+
return [
|
|
26885
|
+
...globalTools,
|
|
26886
|
+
new ProjectWorkListTool(this.projectWork, context),
|
|
26887
|
+
new ProjectWorkGetTool(this.projectWork, context),
|
|
26888
|
+
new ProjectWorkCreateTool(this.projectWork, context),
|
|
26889
|
+
new ProjectWorkUpdateTool(this.projectWork, context)
|
|
26890
|
+
];
|
|
26891
|
+
};
|
|
25795
26892
|
};
|
|
25796
26893
|
//#endregion
|
|
25797
26894
|
//#region src/tools/session-history.tools.ts
|
|
@@ -27628,7 +28725,7 @@ var ToolProviderContribution = class extends Contribution$1 {
|
|
|
27628
28725
|
new DesktopToolProvider(runContextService, this.kernel.extensions.getDesktopHost()),
|
|
27629
28726
|
new CoreToolProvider(runContextService, this.kernel.getGatewayController),
|
|
27630
28727
|
new MessagingToolProvider(runContextService, this.kernel.channels, this.kernel.automation, this.kernel.extensions),
|
|
27631
|
-
new ProjectToolProvider(this.kernel.projectManager),
|
|
28728
|
+
new ProjectToolProvider(runContextService, this.kernel.projectManager, this.kernel.projectWorkManager),
|
|
27632
28729
|
new SessionToolProvider(runContextService, this.kernel.sessionManager, this.kernel.sessionRequests, this.kernel.sessionSearch),
|
|
27633
28730
|
new AssetToolProvider(this.kernel.assetStore),
|
|
27634
28731
|
new ServiceActionToolProvider(runContextService, this.kernel.serviceAppManager),
|
|
@@ -27668,6 +28765,7 @@ function createKernelServiceAppManagers(params) {
|
|
|
27668
28765
|
appHomeDirectory,
|
|
27669
28766
|
configManager,
|
|
27670
28767
|
listPackageComponentSources: appPackageManager.listActiveComponentSources,
|
|
28768
|
+
assertDocumentAccess: appPackageManager.assertDocumentAccess,
|
|
27671
28769
|
capabilityGrantManager,
|
|
27672
28770
|
hasAgent,
|
|
27673
28771
|
providerManager,
|
|
@@ -27724,16 +28822,26 @@ function createPortableRuntimeAcceptanceServices(params) {
|
|
|
27724
28822
|
};
|
|
27725
28823
|
}
|
|
27726
28824
|
function createKernelSessionManagers(params) {
|
|
27727
|
-
const { agentContextWindowManager, agentManager, configManager, eventBus, ingress, observationStorePath, projectStorePath, sessionsDir } = params;
|
|
28825
|
+
const { agentContextWindowManager, agentManager, configManager, eventBus, ingress, observationStorePath, projectStorePath, projectWorkStorePath, sessionsDir } = params;
|
|
27728
28826
|
const sessionSearch = new SessionSearchService({
|
|
27729
28827
|
databasePath: resolve(getDataDir(), "session-search.db"),
|
|
27730
28828
|
sessionsDir
|
|
27731
28829
|
});
|
|
27732
28830
|
const journalStore = new NcpAgentSessionJournalStore(resolve(sessionsDir, ".ncp-agent-journal"));
|
|
28831
|
+
const projectWorkOwner = { current: null };
|
|
27733
28832
|
const projectManager = new ProjectManager({
|
|
27734
28833
|
storePath: projectStorePath,
|
|
27735
|
-
getDefaultWorkspacePath: () => getWorkspacePathFromConfig(configManager.config)
|
|
28834
|
+
getDefaultWorkspacePath: () => getWorkspacePathFromConfig(configManager.config),
|
|
28835
|
+
onProjectRegistered: async (project) => {
|
|
28836
|
+
await projectWorkOwner.current?.ensureProject(project.id);
|
|
28837
|
+
}
|
|
27736
28838
|
});
|
|
28839
|
+
const projectWorkManager = new ProjectWorkManager({
|
|
28840
|
+
databasePath: projectWorkStorePath,
|
|
28841
|
+
eventBus,
|
|
28842
|
+
projectManager
|
|
28843
|
+
});
|
|
28844
|
+
projectWorkOwner.current = projectWorkManager;
|
|
27737
28845
|
const observationOwner = { current: null };
|
|
27738
28846
|
const sessionManager = new SessionManager({
|
|
27739
28847
|
agentContextWindowManager,
|
|
@@ -27766,6 +28874,7 @@ function createKernelSessionManagers(params) {
|
|
|
27766
28874
|
observations,
|
|
27767
28875
|
projectManager,
|
|
27768
28876
|
projectObservation,
|
|
28877
|
+
projectWorkManager,
|
|
27769
28878
|
sessionManager,
|
|
27770
28879
|
sessionSearch
|
|
27771
28880
|
};
|
|
@@ -27794,6 +28903,10 @@ function installKernelAppPackageRuntimeHooks(params) {
|
|
|
27794
28903
|
panelAppManager.deactivatePackageComponents(sources);
|
|
27795
28904
|
await serviceAppManager.deactivatePackageComponents(sources);
|
|
27796
28905
|
},
|
|
28906
|
+
prepareCapabilityChange: async (sources) => await serviceAppManager.preparePackageComponentDeactivation(sources),
|
|
28907
|
+
afterCapabilityChange: async (sources) => {
|
|
28908
|
+
await serviceAppManager.activatePackageComponents(sources);
|
|
28909
|
+
},
|
|
27797
28910
|
beforeUninstall: async (sources) => {
|
|
27798
28911
|
const rollbacks = [];
|
|
27799
28912
|
try {
|
|
@@ -27862,6 +28975,7 @@ var NextclawKernel = class {
|
|
|
27862
28975
|
preferenceManager;
|
|
27863
28976
|
projectManager;
|
|
27864
28977
|
projectObservation;
|
|
28978
|
+
projectWorkManager;
|
|
27865
28979
|
serviceAppManager;
|
|
27866
28980
|
extensions;
|
|
27867
28981
|
agentRuntimeManager = new AgentRuntimeManager();
|
|
@@ -27907,7 +29021,7 @@ var NextclawKernel = class {
|
|
|
27907
29021
|
homeDir: options.homeDir
|
|
27908
29022
|
});
|
|
27909
29023
|
this.capabilityGrantLegacyMigration = this.createCapabilityGrantLegacyMigration(options);
|
|
27910
|
-
({journalStore: this.ncpAgentSessionJournalStore, observations: this.observations, projectManager: this.projectManager, projectObservation: this.projectObservation, sessionManager: this.sessionManager, sessionSearch: this.sessionSearch} = createKernelSessionManagers({
|
|
29024
|
+
({journalStore: this.ncpAgentSessionJournalStore, observations: this.observations, projectManager: this.projectManager, projectObservation: this.projectObservation, projectWorkManager: this.projectWorkManager, sessionManager: this.sessionManager, sessionSearch: this.sessionSearch} = createKernelSessionManagers({
|
|
27911
29025
|
agentContextWindowManager: this.agentContextWindowManager,
|
|
27912
29026
|
agentManager: this.agents,
|
|
27913
29027
|
configManager: this.configManager,
|
|
@@ -27915,6 +29029,7 @@ var NextclawKernel = class {
|
|
|
27915
29029
|
ingress: this.ingress,
|
|
27916
29030
|
observationStorePath: resolveKernelObservationStorePath(options),
|
|
27917
29031
|
projectStorePath: resolveKernelProjectStorePath(options),
|
|
29032
|
+
projectWorkStorePath: resolveKernelProjectWorkStorePath(options),
|
|
27918
29033
|
sessionsDir
|
|
27919
29034
|
}));
|
|
27920
29035
|
this.inboxDeliveryManager = new InboxDeliveryManager({
|
|
@@ -28016,6 +29131,7 @@ var NextclawKernel = class {
|
|
|
28016
29131
|
this.providerModelCatalog.start();
|
|
28017
29132
|
await this.projectManager.migrateLegacyProjects();
|
|
28018
29133
|
await this.projectManager.importSessionProjects((await this.sessionManager.listSessions()).map((session) => readProjectRoot(session.metadata)));
|
|
29134
|
+
await this.projectWorkManager.initialize();
|
|
28019
29135
|
await this.sessionManager.start();
|
|
28020
29136
|
for (const contribution of this.contributions) await contribution.start();
|
|
28021
29137
|
this.agentRunRequestManager.start();
|
|
@@ -28035,6 +29151,7 @@ var NextclawKernel = class {
|
|
|
28035
29151
|
await this.mcpManager.dispose();
|
|
28036
29152
|
await this.serviceAppManager.dispose();
|
|
28037
29153
|
await this.sessionSearch.dispose();
|
|
29154
|
+
this.projectWorkManager.dispose();
|
|
28038
29155
|
};
|
|
28039
29156
|
};
|
|
28040
29157
|
//#endregion
|
|
@@ -29177,6 +30294,6 @@ function resolveLegacyEventType(message) {
|
|
|
29177
30294
|
return `message.${role || "other"}`;
|
|
29178
30295
|
}
|
|
29179
30296
|
//#endregion
|
|
29180
|
-
export { AUTOMATIC_UPDATE_CHECK_INTERVAL_MS, AccessManager, AgentManager, AgentRunClient, AppDataError, AppDataManager, AppPackageError, AppPackageManager, AutomationManager, BuiltinNarpRuntimeProviderService, CONTEXT_COMPACTION_CONTINUATION_TEXT, CONTEXT_COMPACTION_PROJECTION_KIND, CONTEXT_COMPACTION_PROJECTION_METADATA_KEY, CONTEXT_COMPACTION_SYSTEM_PREAMBLE, CONTEXT_COMPACTION_TIMELINE_KIND, CapabilityGrantLegacyMigrationService, CapabilityGrantManager, CapabilityGrantStore, ChannelManager, CommandRegistry, ConfigManager, ContextCompactionJournalRecoveryService, ContextCompactionPreflightService, Contribution, DEFAULT_AGENT_RUNTIME_ENTRY_ID, DEFAULT_SERVICE_ACTION_RISK, DESKTOP_HOST_ACCESS, DESKTOP_HOST_PROTOCOL_VERSION, DesktopHostCapabilityManager, DesktopNodeReplService, DesktopSessionStateService, EventBus, ExtensionManager, FeatureControlsService, GatewayInboundProcessor, InboxDeliveryError, InboxDeliveryManager, Ingress, LlmProviderManager, LlmUsageManager, LlmUsageStore, MAX_INBOX_DELIVERY_CONTENT_LENGTH, McpManager, McpServiceAppRuntimeService, NARP_HTTP_RUNTIME_KIND, NARP_STDIO_RUNTIME_KIND, NEXTCLAW_TIMELINE_KIND_METADATA_KEY, NcpAgentSessionJournalStore, NextclawHarness, NextclawHarnessError, NextclawKernel, ObservationManager, PANEL_APP_AGENT_CAPABILITIES, PORTABLE_RUNTIME_ACCEPTANCE_CONTRACT, PORTABLE_RUNTIME_ACCEPTANCE_CONTRACT_FINGERPRINT, PORTABLE_RUNTIME_ACCEPTANCE_LOCALES, PORTABLE_RUNTIME_ACCEPTANCE_PLATFORMS, PORTABLE_RUNTIME_ACCEPTANCE_PRESENTATION, PORTABLE_RUNTIME_ACCEPTANCE_REFERENCE_APP_ID, PROJECT_OBSERVATION_PROTOCOL, PROJECT_TEMPLATE_IDS, PROVIDER_MODEL_CATALOG_REFRESH_INTERVAL_MS, PanelAppAssetTokenService, PanelAppError, PanelAppManager, PortableRuntimeAcceptanceIdentityService, PortableRuntimeAcceptanceManager, PreferenceError, PreferenceManager, ProjectError, ProjectManager, ProjectObservationError, ProjectObservationService, ProviderManagerNcpLLMApi, ProviderModelCatalogManager, SERVICE_APP_MANIFEST_FILE_NAME, ServiceAppAiCapabilityService, ServiceAppError, ServiceAppJobJournalService, ServiceAppManager, ServiceAppResidentEventInboxService, ServiceAppRuntimeService, SessionContextCompactionError, SessionContextCompactionManager, SessionManager, SessionMessageCursorError, SessionRequestManager, SessionSettingsError, SkillManager, SystemObjectReferenceError, SystemObjectReferenceManager, UnavailableDesktopHost, UpdateManifestReader, VerificationRecordService, assertObservationJsonValue, assertObservationPredicate, buildAgentRunSendPayload, buildContextCompactionModelProjection, buildContextCompactionTimelineNcpMessage, buildLlmUsageSummary, buildLocalizedTextMap, buildNextclawNcpRunContext, buildObservationEventModelMessage, buildServiceActionId, buildSessionRequestCompletionMessage, capabilityGrantCovers, createAgentRuntimeSessionRequestDispatcher, createAgentRuntimeSessionRequestSourceNotifier, createAssetTools, createCapabilityDeclarationFingerprint, createContextCompactionMessageId, createContextWindowSignature, createCronJobSystemObjectProvider, createDesktopHostError, createInboxDeliverySystemObjectProvider, createLlmUsageRecord, createPanelAppAgentGrantRequest, createPanelAppClientGrantRequest, createServiceActionGrantRequest, createServiceAppAgentSlotGrantRequest, createServiceAppModelSlotGrantRequest, createTypedKey, describeAgentRuntimeSessionTypes, dispatchAgentRuntimeSessionRequest, dispatchChannelReplyRoute, dispatchPromptOverNcp, dispatchPromptOverNcpResult, evaluateObservationEventAdmission, evaluatePortableRuntimeAcceptance, evaluatePortableRuntimeAcceptanceArtifact, eventKeys, getAutomaticUpdateCheckDelay, getCapabilityGrantKey, getServiceActionName, getServiceAppManifestPath, getUiContentParamsBootstrapScript, getUnsignedUpdateManifest, hasLlmUsageTelemetry, ingressKeys, injectUiContentParamsBootstrap, isAppDataError, isAppPackageError, isContextCompactionProjectionMessage, isContextCompactionTimelineMessage, isContextWindowSnapshot, isInboxDeliveryError, isPanelAppAgentCapability, isPanelAppError, isPreferenceError, isProjectError, isProjectObservationError, isReplyCapableChannel, isServiceAppError, isSessionContextCompactionError, isSessionMessageCursorError, isSessionSettingsError, isSystemObjectReferenceError, listExtensionChannelIds, listServiceAppManifestActions, matchesCapabilityGrantFilter, matchesObservationPredicate, mergeServiceAppRuntimeActions, normalizeAgentRuntimeSessionTypeIcon, normalizeCapabilityGrantRequest, normalizeLlmUsageModel, normalizeOptionalString, normalizePortableRuntimeEnvironment, parseObservationDuration, parsePortableRuntimeAcceptanceEvidenceArtifact, parseServiceAppManifest, parseSkillFrontmatter, presentPortableRuntimeAcceptanceDefinition, readContextCompactionCheckpoint, readContextWindowEventSessionId, readJsonPointer, readLatestContextCompactionCheckpoint, readLearningLoopRuntimeConfig, readMetadataModel, readMetadataThinking, readServiceActionTargetId, readServiceAppManifest, readServiceAppSlotTarget, resolveAgentRuntimeEntries, resolveAutomaticUpdateCheckIntervalMs, resolveChannelReplyRoute, resolveEffectiveModel, resolveLegacyEventType, resolvePortableRuntimeAcceptanceLocale, resolveSessionChannelContext, runGatewayInboundLoop, runNextclawTask, sanitizeLlmUsage, serializeContextTail, serializeUnsignedUpdateManifest, shouldRefreshContextWindowDuringStream, shouldRefreshContextWindowImmediately, startPromptOverNcpExecution, stripSkillFrontmatter, syncSessionThinkingPreference, toBoundedJson, toNcpMessages, waitForAgentRuntimeSessionReply };
|
|
30297
|
+
export { AUTOMATIC_UPDATE_CHECK_INTERVAL_MS, AccessManager, AgentManager, AgentRunClient, AppDataError, AppDataManager, AppPackageError, AppPackageManager, AutomationManager, BuiltinNarpRuntimeProviderService, CONTEXT_COMPACTION_CONTINUATION_TEXT, CONTEXT_COMPACTION_PROJECTION_KIND, CONTEXT_COMPACTION_PROJECTION_METADATA_KEY, CONTEXT_COMPACTION_SYSTEM_PREAMBLE, CONTEXT_COMPACTION_TIMELINE_KIND, CapabilityGrantLegacyMigrationService, CapabilityGrantManager, CapabilityGrantStore, ChannelManager, CommandRegistry, ConfigManager, ContextCompactionJournalRecoveryService, ContextCompactionPreflightService, Contribution, DEFAULT_AGENT_RUNTIME_ENTRY_ID, DEFAULT_SERVICE_ACTION_RISK, DESKTOP_HOST_ACCESS, DESKTOP_HOST_PROTOCOL_VERSION, DesktopHostCapabilityManager, DesktopNodeReplService, DesktopSessionStateService, EventBus, ExtensionManager, FeatureControlsService, GatewayInboundProcessor, InboxDeliveryError, InboxDeliveryManager, Ingress, LlmProviderManager, LlmUsageManager, LlmUsageStore, MAX_INBOX_DELIVERY_CONTENT_LENGTH, McpManager, McpServiceAppRuntimeService, NARP_HTTP_RUNTIME_KIND, NARP_STDIO_RUNTIME_KIND, NEXTCLAW_TIMELINE_KIND_METADATA_KEY, NcpAgentSessionJournalStore, NextclawHarness, NextclawHarnessError, NextclawKernel, ObservationManager, PANEL_APP_AGENT_CAPABILITIES, PORTABLE_RUNTIME_ACCEPTANCE_CONTRACT, PORTABLE_RUNTIME_ACCEPTANCE_CONTRACT_FINGERPRINT, PORTABLE_RUNTIME_ACCEPTANCE_LOCALES, PORTABLE_RUNTIME_ACCEPTANCE_PLATFORMS, PORTABLE_RUNTIME_ACCEPTANCE_PRESENTATION, PORTABLE_RUNTIME_ACCEPTANCE_REFERENCE_APP_ID, PROJECT_OBSERVATION_PROTOCOL, PROJECT_TEMPLATE_IDS, PROJECT_WORK_ATTENTION_VALUES, PROJECT_WORK_STATE_CATEGORIES, PROVIDER_MODEL_CATALOG_REFRESH_INTERVAL_MS, PanelAppAssetTokenService, PanelAppError, PanelAppManager, PortableRuntimeAcceptanceIdentityService, PortableRuntimeAcceptanceManager, PreferenceError, PreferenceManager, ProjectError, ProjectManager, ProjectObservationError, ProjectObservationService, ProjectWorkError, ProjectWorkManager, ProviderManagerNcpLLMApi, ProviderModelCatalogManager, SERVICE_APP_MANIFEST_FILE_NAME, ServiceAppAiCapabilityService, ServiceAppError, ServiceAppJobJournalService, ServiceAppManager, ServiceAppResidentEventInboxService, ServiceAppRuntimeService, SessionContextCompactionError, SessionContextCompactionManager, SessionManager, SessionMessageCursorError, SessionRequestManager, SessionSettingsError, SkillManager, SystemObjectReferenceError, SystemObjectReferenceManager, UnavailableDesktopHost, UpdateManifestReader, VerificationRecordService, assertObservationJsonValue, assertObservationPredicate, buildAgentRunSendPayload, buildContextCompactionModelProjection, buildContextCompactionTimelineNcpMessage, buildLlmUsageSummary, buildLocalizedTextMap, buildNextclawNcpRunContext, buildObservationEventModelMessage, buildServiceActionId, buildSessionRequestCompletionMessage, capabilityGrantCovers, createAgentRuntimeSessionRequestDispatcher, createAgentRuntimeSessionRequestSourceNotifier, createAssetTools, createCapabilityDeclarationFingerprint, createContextCompactionMessageId, createContextWindowSignature, createCronJobSystemObjectProvider, createDesktopHostError, createInboxDeliverySystemObjectProvider, createLlmUsageRecord, createPanelAppAgentGrantRequest, createPanelAppClientGrantRequest, createServiceActionGrantRequest, createServiceAppAgentSlotGrantRequest, createServiceAppModelSlotGrantRequest, createTypedKey, describeAgentRuntimeSessionTypes, dispatchAgentRuntimeSessionRequest, dispatchChannelReplyRoute, dispatchPromptOverNcp, dispatchPromptOverNcpResult, evaluateObservationEventAdmission, evaluatePortableRuntimeAcceptance, evaluatePortableRuntimeAcceptanceArtifact, eventKeys, getAutomaticUpdateCheckDelay, getCapabilityGrantKey, getServiceActionName, getServiceAppManifestPath, getUiContentParamsBootstrapScript, getUnsignedUpdateManifest, hasLlmUsageTelemetry, ingressKeys, injectUiContentParamsBootstrap, isAppDataError, isAppPackageError, isContextCompactionProjectionMessage, isContextCompactionTimelineMessage, isContextWindowSnapshot, isInboxDeliveryError, isPanelAppAgentCapability, isPanelAppError, isPreferenceError, isProjectError, isProjectObservationError, isProjectWorkError, isReplyCapableChannel, isServiceAppError, isSessionContextCompactionError, isSessionMessageCursorError, isSessionSettingsError, isSystemObjectReferenceError, listExtensionChannelIds, listServiceAppManifestActions, matchesCapabilityGrantFilter, matchesObservationPredicate, mergeServiceAppRuntimeActions, normalizeAgentRuntimeSessionTypeIcon, normalizeCapabilityGrantRequest, normalizeLlmUsageModel, normalizeOptionalString, normalizePortableRuntimeEnvironment, parseObservationDuration, parsePortableRuntimeAcceptanceEvidenceArtifact, parseServiceAppManifest, parseSkillFrontmatter, presentPortableRuntimeAcceptanceDefinition, readContextCompactionCheckpoint, readContextWindowEventSessionId, readJsonPointer, readLatestContextCompactionCheckpoint, readLearningLoopRuntimeConfig, readMetadataModel, readMetadataThinking, readServiceActionTargetId, readServiceAppManifest, readServiceAppSlotTarget, resolveAgentRuntimeEntries, resolveAutomaticUpdateCheckIntervalMs, resolveChannelReplyRoute, resolveEffectiveModel, resolveLegacyEventType, resolvePortableRuntimeAcceptanceLocale, resolveSessionChannelContext, runGatewayInboundLoop, runNextclawTask, sanitizeLlmUsage, serializeContextTail, serializeUnsignedUpdateManifest, shouldRefreshContextWindowDuringStream, shouldRefreshContextWindowImmediately, startPromptOverNcpExecution, stripSkillFrontmatter, syncSessionThinkingPreference, toBoundedJson, toNcpMessages, waitForAgentRuntimeSessionReply };
|
|
29181
30298
|
|
|
29182
30299
|
//# sourceMappingURL=index.js.map
|