@dharma-ai-labs/agent-fabric 0.2.96 → 0.2.98
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 +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +351 -297
- package/dist/index.js.map +1 -1
- package/dist/index.test.js +36 -3
- package/dist/index.test.js.map +1 -1
- package/dist/repositorySourceSync.d.ts +8 -0
- package/dist/repositorySourceSync.d.ts.map +1 -1
- package/dist/repositorySourceSync.js +26 -0
- package/dist/repositorySourceSync.js.map +1 -1
- package/dist/repositorySourceSync.test.js +35 -1
- package/dist/repositorySourceSync.test.js.map +1 -1
- package/dist/skillPreparationCache.test.js +1 -0
- package/dist/skillPreparationCache.test.js.map +1 -1
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -19,7 +19,7 @@ import { CLI_USAGE } from './usage.js';
|
|
|
19
19
|
import { initializeRepositoryKnowledge, readRepositoryKnowledgeSource } from './repositoryKnowledge.js';
|
|
20
20
|
import { inventoryRepositoryPackage, readRepositoryPackageSnapshot, readRepositorySourceBaselineSnapshot, writeRepositoryPackageSnapshot } from './repositoryPackage.js';
|
|
21
21
|
import { validateRepositorySourceAuthorization } from './repositorySourceAuthorization.js';
|
|
22
|
-
import { advanceRepositorySourceBaseline, BlockedRepositorySourceRetry, fetchRepositorySourceAuthorization, RepositorySourceWatcher, scanRepositorySourceChanges, seedRepositorySourceWatcher } from './repositorySourceSync.js';
|
|
22
|
+
import { advanceRepositorySourceBaseline, BlockedRepositorySourceRetry, fetchRepositorySourceAuthorization, recoverPublishedLocalSourceBaseline, RepositorySourceWatcher, scanRepositorySourceChanges, seedRepositorySourceWatcher } from './repositorySourceSync.js';
|
|
23
23
|
import { assertRepositoryInstallerOwnership, writeRepositoryInstallerFile } from './repositoryInstallerFiles.js';
|
|
24
24
|
import { receiveRepositoryPackageDelivery } from './repositoryPackageDelivery.js';
|
|
25
25
|
import { selectInstalledRepositoryKnowledge } from './repositoryInstalledKnowledge.js';
|
|
@@ -37,7 +37,7 @@ import { connectDemoDevice, verifyDemoDevice } from './demoEnrollment.js';
|
|
|
37
37
|
import { performDemoPeerAction, withDemoDeviceLock } from './demoPeer.js';
|
|
38
38
|
import { demoRepositoryPackage } from './demoPackage.js';
|
|
39
39
|
import { runDemoWatch } from './demoWatch.js';
|
|
40
|
-
const VERSION = '0.2.
|
|
40
|
+
const VERSION = '0.2.98';
|
|
41
41
|
const USAGE = CLI_USAGE;
|
|
42
42
|
const execFileAsync = promisify(execFile);
|
|
43
43
|
const LOCAL_PROVIDER_IDS = ['codex', 'claude', 'agy', 'hermes'];
|
|
@@ -752,6 +752,30 @@ export async function relayProcessState(home = dharmaHome()) {
|
|
|
752
752
|
return code === 'EPERM' ? 'unknown' : 'stopped';
|
|
753
753
|
}
|
|
754
754
|
}
|
|
755
|
+
export async function acquireRelayProcessLease(home = dharmaHome()) {
|
|
756
|
+
const pidPath = resolve(home, 'relay', 'relay.pid');
|
|
757
|
+
const releaseLock = await acquirePidLock(`${pidPath}.lock`, 250, 'Another relay process is already running for this device.');
|
|
758
|
+
try {
|
|
759
|
+
const state = await relayProcessState(home);
|
|
760
|
+
if (state !== 'stopped')
|
|
761
|
+
throw new Error(`Cannot start a second relay while process state is ${state}.`);
|
|
762
|
+
await writeFile(pidPath, `${process.pid}\n`, { mode: 0o600 });
|
|
763
|
+
}
|
|
764
|
+
catch (error) {
|
|
765
|
+
await releaseLock();
|
|
766
|
+
throw error;
|
|
767
|
+
}
|
|
768
|
+
return async () => {
|
|
769
|
+
try {
|
|
770
|
+
if ((await readFile(pidPath, 'utf8').catch(() => '')).trim() === String(process.pid)) {
|
|
771
|
+
await rm(pidPath, { force: true });
|
|
772
|
+
}
|
|
773
|
+
}
|
|
774
|
+
finally {
|
|
775
|
+
await releaseLock();
|
|
776
|
+
}
|
|
777
|
+
};
|
|
778
|
+
}
|
|
755
779
|
export async function materializeWorkspacePolicy(input) {
|
|
756
780
|
const allowedCommands = {};
|
|
757
781
|
try {
|
|
@@ -1396,9 +1420,17 @@ export async function waitForRelayReadiness(options = {}) {
|
|
|
1396
1420
|
lastState = await processState();
|
|
1397
1421
|
if (lastState === 'running') {
|
|
1398
1422
|
try {
|
|
1399
|
-
|
|
1423
|
+
const acknowledged = await probe();
|
|
1424
|
+
if (!acknowledged.connected)
|
|
1425
|
+
throw new Error('Relay session is not connected.');
|
|
1426
|
+
if (options.expectedVersion && acknowledged.relayVersion !== options.expectedVersion) {
|
|
1427
|
+
throw new Error(`relay_version_mismatch:${acknowledged.relayVersion}:${options.expectedVersion}`);
|
|
1428
|
+
}
|
|
1429
|
+
return { state: 'running', probe: acknowledged };
|
|
1400
1430
|
}
|
|
1401
|
-
catch {
|
|
1431
|
+
catch (error) {
|
|
1432
|
+
if (String(error.message).startsWith('relay_version_mismatch:'))
|
|
1433
|
+
throw error;
|
|
1402
1434
|
acknowledgementPending = true;
|
|
1403
1435
|
}
|
|
1404
1436
|
}
|
|
@@ -1419,7 +1451,7 @@ async function startRelayDaemon(policyPath) {
|
|
|
1419
1451
|
});
|
|
1420
1452
|
child.unref();
|
|
1421
1453
|
}
|
|
1422
|
-
const readiness = await waitForRelayReadiness();
|
|
1454
|
+
const readiness = await waitForRelayReadiness({ expectedVersion: VERSION });
|
|
1423
1455
|
return { started: !alreadyRunning, ...readiness };
|
|
1424
1456
|
}
|
|
1425
1457
|
export function providerHintFromEnvironment(env = process.env) {
|
|
@@ -5185,330 +5217,352 @@ async function relayStart(flags) {
|
|
|
5185
5217
|
const fabric = await client();
|
|
5186
5218
|
const leaseSeconds = Number(flags.get('lease-seconds') || 120);
|
|
5187
5219
|
const pollMs = Math.min(Math.max(Number(flags.get('poll-seconds') || 3), 1), 60) * 1_000;
|
|
5188
|
-
const
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
5216
|
-
|
|
5217
|
-
|
|
5218
|
-
|
|
5219
|
-
|
|
5220
|
-
|
|
5221
|
-
|
|
5222
|
-
|
|
5220
|
+
const releaseRelayLease = await acquireRelayProcessLease();
|
|
5221
|
+
try {
|
|
5222
|
+
let stopping = false;
|
|
5223
|
+
let stopPreparation;
|
|
5224
|
+
const stop = () => { stopping = true; stopPreparation?.(); };
|
|
5225
|
+
process.once('SIGINT', stop);
|
|
5226
|
+
process.once('SIGTERM', stop);
|
|
5227
|
+
let tasksCompleted = 0;
|
|
5228
|
+
let lastProviderFailureCategory = null;
|
|
5229
|
+
let taskTrajectoriesRecovered = 0;
|
|
5230
|
+
let evidenceResponsesCompleted = 0;
|
|
5231
|
+
let trajectorySyncsCompleted = 0;
|
|
5232
|
+
let nextPolicyRefreshAt = 0;
|
|
5233
|
+
let evidencePolicyFresh = false;
|
|
5234
|
+
const repositorySourceWatcher = new RepositorySourceWatcher();
|
|
5235
|
+
const blockedRepositorySourceRetry = new BlockedRepositorySourceRetry();
|
|
5236
|
+
let repositorySourceBaselineAvailable = true;
|
|
5237
|
+
let publishedLocalSnapshotHash = canonicalWorkspace.repositoryPackage?.publishedLocalSnapshotHash
|
|
5238
|
+
|| (canonicalWorkspace.repositoryPackage?.state === 'published'
|
|
5239
|
+
&& canonicalWorkspace.repositoryPackage.snapshotHash === canonicalWorkspace.repositoryPackage.localBaselineSnapshotHash
|
|
5240
|
+
? canonicalWorkspace.repositoryPackage.snapshotHash : null);
|
|
5241
|
+
if (canonicalWorkspace.repositoryPackage && canonicalWorkspace.repositoryBindingId && canonicalWorkspace.repositoryAgentId) {
|
|
5242
|
+
try {
|
|
5243
|
+
const recovered = await recoverPublishedLocalSourceBaseline({
|
|
5244
|
+
workspace: canonicalWorkspace.path, organizationId: canonicalWorkspace.organizationId,
|
|
5245
|
+
workspaceId: canonicalWorkspace.workspaceId, repositoryBindingId: canonicalWorkspace.repositoryBindingId,
|
|
5246
|
+
repositoryAgentId: canonicalWorkspace.repositoryAgentId, record: canonicalWorkspace.repositoryPackage,
|
|
5247
|
+
});
|
|
5248
|
+
if (recovered && recovered !== publishedLocalSnapshotHash) {
|
|
5249
|
+
publishedLocalSnapshotHash = recovered;
|
|
5250
|
+
canonicalWorkspace = { ...canonicalWorkspace, repositoryPackage: {
|
|
5251
|
+
...canonicalWorkspace.repositoryPackage, localBaselineSnapshotHash: recovered,
|
|
5252
|
+
publishedLocalSnapshotHash: recovered,
|
|
5253
|
+
} };
|
|
5254
|
+
await saveWorkspaceRecord(canonicalWorkspace);
|
|
5255
|
+
}
|
|
5256
|
+
}
|
|
5257
|
+
catch {
|
|
5258
|
+
repositorySourceBaselineAvailable = false;
|
|
5223
5259
|
}
|
|
5224
5260
|
}
|
|
5225
|
-
|
|
5226
|
-
|
|
5261
|
+
if (publishedLocalSnapshotHash) {
|
|
5262
|
+
try {
|
|
5263
|
+
await readRepositoryPackageSnapshot(canonicalWorkspace.path, publishedLocalSnapshotHash);
|
|
5264
|
+
const localHash = canonicalWorkspace.repositoryPackage?.localBaselineSnapshotHash;
|
|
5265
|
+
if (localHash && (localHash === publishedLocalSnapshotHash
|
|
5266
|
+
|| canonicalWorkspace.repositoryPackage?.pendingLocalSnapshotHash === localHash)) {
|
|
5267
|
+
await seedRepositorySourceWatcher({ workspace: canonicalWorkspace.path,
|
|
5268
|
+
organizationId: canonicalWorkspace.organizationId, workspaceId: canonicalWorkspace.workspaceId,
|
|
5269
|
+
repositoryBindingId: canonicalWorkspace.repositoryBindingId,
|
|
5270
|
+
repositoryAgentId: canonicalWorkspace.repositoryAgentId,
|
|
5271
|
+
publishedHash: publishedLocalSnapshotHash,
|
|
5272
|
+
localHash,
|
|
5273
|
+
watcher: repositorySourceWatcher });
|
|
5274
|
+
}
|
|
5275
|
+
}
|
|
5276
|
+
catch {
|
|
5277
|
+
repositorySourceBaselineAvailable = false;
|
|
5278
|
+
}
|
|
5227
5279
|
}
|
|
5228
|
-
|
|
5229
|
-
|
|
5230
|
-
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
5234
|
-
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
|
|
5250
|
-
|
|
5251
|
-
if (capability.skillInstall === 'unavailable' || !isLocalProviderId(adapter.providerId))
|
|
5252
|
-
return;
|
|
5253
|
-
const provider = adapter.providerId;
|
|
5254
|
-
await withSkillPreparationTransaction({ home: dharmaHome(), workspaceId: canonicalWorkspace.workspaceId,
|
|
5255
|
-
provider, assertCurrent: assertRunning }, async () => {
|
|
5256
|
-
const prepared = await prepareSkillUpdate({ workspaceId: canonicalWorkspace.workspaceId,
|
|
5257
|
-
provider, policyPath, fabric, automatic: true, assertRunning });
|
|
5258
|
-
if (!prepared) {
|
|
5259
|
-
assertRunning();
|
|
5280
|
+
let nextRepositorySourceScanAt = 0;
|
|
5281
|
+
let repositorySourceCandidates = 0;
|
|
5282
|
+
let repositorySourceFailures = 0;
|
|
5283
|
+
let repositorySourceState = 'not_scanned';
|
|
5284
|
+
const { LocalVault, loadOrCreateVaultMasterKey } = await loadVaultModule();
|
|
5285
|
+
const vault = await LocalVault.open({
|
|
5286
|
+
root: resolve(dharmaHome(), 'vault'),
|
|
5287
|
+
masterKey: await loadOrCreateVaultMasterKey(),
|
|
5288
|
+
rawLocalDays: rawLocalRetentionDays(policy),
|
|
5289
|
+
});
|
|
5290
|
+
let skillPreparationsCompleted = 0;
|
|
5291
|
+
let skillPreparationFailures = 0;
|
|
5292
|
+
let skillActivationsCompleted = 0;
|
|
5293
|
+
let skillActivationFailures = 0;
|
|
5294
|
+
const skillActivationFailuresByProvider = {};
|
|
5295
|
+
let nextSkillActivationAt = 0;
|
|
5296
|
+
const skillPreparationPump = startSkillPreparationPump({
|
|
5297
|
+
prepare: async (assertRunning) => {
|
|
5298
|
+
await prepareProvidersIndependently(providerAdapters, assertRunning, async (adapter) => {
|
|
5299
|
+
assertRunning();
|
|
5300
|
+
const capability = await adapter.capability();
|
|
5301
|
+
assertRunning();
|
|
5302
|
+
if (capability.skillInstall === 'unavailable' || !isLocalProviderId(adapter.providerId))
|
|
5260
5303
|
return;
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
prepared.
|
|
5265
|
-
|
|
5266
|
-
if (!
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
|
|
5273
|
-
|
|
5274
|
-
|
|
5275
|
-
|
|
5276
|
-
|
|
5277
|
-
|
|
5278
|
-
|
|
5279
|
-
await writeFile(resolve(prepared.sourceRoot, 'PREPARED.json'), pendingRecord, { mode: 0o600, flag: 'wx' });
|
|
5280
|
-
prepared.assertCurrent();
|
|
5281
|
-
await publishSkillPreparationCache({ home: dharmaHome(), workspaceId: canonicalWorkspace.workspaceId,
|
|
5282
|
-
provider, sourceRoot: prepared.sourceRoot, recordBytes: pendingRecord, assertCurrent: prepared.assertCurrent,
|
|
5283
|
-
expected: { organizationId: prepared.config.organizationId, deviceId: prepared.config.deviceId,
|
|
5304
|
+
const provider = adapter.providerId;
|
|
5305
|
+
await withSkillPreparationTransaction({ home: dharmaHome(), workspaceId: canonicalWorkspace.workspaceId,
|
|
5306
|
+
provider, assertCurrent: assertRunning }, async () => {
|
|
5307
|
+
const prepared = await prepareSkillUpdate({ workspaceId: canonicalWorkspace.workspaceId,
|
|
5308
|
+
provider, policyPath, fabric, automatic: true, assertRunning });
|
|
5309
|
+
if (!prepared) {
|
|
5310
|
+
assertRunning();
|
|
5311
|
+
return;
|
|
5312
|
+
}
|
|
5313
|
+
let retained = false;
|
|
5314
|
+
try {
|
|
5315
|
+
prepared.assertCurrent();
|
|
5316
|
+
const repositoryAgentId = prepared.workspace.repositoryAgentId;
|
|
5317
|
+
if (!repositoryAgentId)
|
|
5318
|
+
throw new Error('Preparation cache requires an enrolled repository agent.');
|
|
5319
|
+
const pendingRecord = await serializeSkillPreparationRecord({
|
|
5320
|
+
schema: 'dharma.skill-preparation/v1', organizationId: prepared.config.organizationId,
|
|
5321
|
+
deviceId: prepared.config.deviceId, workspaceId: prepared.workspace.workspaceId,
|
|
5284
5322
|
repositoryAgentId,
|
|
5285
|
-
repositoryBindingId: prepared.workspace.repositoryBindingId ?? null,
|
|
5323
|
+
repositoryBindingId: prepared.workspace.repositoryBindingId ?? null, provider,
|
|
5286
5324
|
policyHash: sha256(canonicalize(prepared.policy)), rolloutId: prepared.rollout.id,
|
|
5287
|
-
|
|
5288
|
-
|
|
5289
|
-
|
|
5290
|
-
|
|
5291
|
-
|
|
5292
|
-
|
|
5293
|
-
|
|
5294
|
-
|
|
5295
|
-
|
|
5296
|
-
|
|
5297
|
-
|
|
5298
|
-
|
|
5299
|
-
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
try {
|
|
5304
|
-
const recoveredTaskTrajectories = await deferUnavailableRelayRetention(async () => finalizeRecoveredSignedTaskTrajectories(fabric, policy));
|
|
5305
|
-
if (recoveredTaskTrajectories.state === 'completed') {
|
|
5306
|
-
taskTrajectoriesRecovered += recoveredTaskTrajectories.value.length;
|
|
5307
|
-
}
|
|
5308
|
-
do {
|
|
5309
|
-
if (Date.now() >= nextPolicyRefreshAt) {
|
|
5310
|
-
try {
|
|
5311
|
-
await syncWorkspacePolicy(fabric, canonicalWorkspace, policy.revision, true);
|
|
5312
|
-
policy = await loadVerifiedWorkspacePolicy(policyPath, canonicalWorkspace.workspaceId);
|
|
5313
|
-
evidencePolicyFresh = true;
|
|
5314
|
-
}
|
|
5315
|
-
catch {
|
|
5316
|
-
evidencePolicyFresh = false;
|
|
5317
|
-
}
|
|
5318
|
-
nextPolicyRefreshAt = Date.now() + 60_000;
|
|
5319
|
-
}
|
|
5320
|
-
if (canonicalWorkspace.repositoryBindingId && canonicalWorkspace.repositoryAgentId) {
|
|
5321
|
-
try {
|
|
5322
|
-
const current = await pollRepositoryCandidate({ transport: fabric,
|
|
5323
|
-
outboxRoot: resolve(dharmaHome(), 'relay', 'repository-candidates'),
|
|
5324
|
-
scope: { organizationId: canonicalWorkspace.organizationId, workspaceId: canonicalWorkspace.workspaceId,
|
|
5325
|
-
repositoryBindingId: canonicalWorkspace.repositoryBindingId,
|
|
5326
|
-
repositoryAgentId: canonicalWorkspace.repositoryAgentId } });
|
|
5327
|
-
if (current && canonicalWorkspace.repositoryPackage) {
|
|
5328
|
-
if (current.candidateId !== canonicalWorkspace.repositoryPackage.candidateId
|
|
5329
|
-
|| current.operationId !== canonicalWorkspace.repositoryPackage.operationId) {
|
|
5330
|
-
throw new Error('Repository candidate status changed identity while polling.');
|
|
5325
|
+
bundle: prepared.bundle, repositoryPackage: prepared.repositoryDelivery
|
|
5326
|
+
? { envelope: prepared.repositoryDelivery.envelope, index: prepared.repositoryDelivery.index } : null,
|
|
5327
|
+
preparedAt: new Date().toISOString(), activationAuthorized: false,
|
|
5328
|
+
});
|
|
5329
|
+
prepared.assertCurrent();
|
|
5330
|
+
await writeFile(resolve(prepared.sourceRoot, 'PREPARED.json'), pendingRecord, { mode: 0o600, flag: 'wx' });
|
|
5331
|
+
prepared.assertCurrent();
|
|
5332
|
+
await publishSkillPreparationCache({ home: dharmaHome(), workspaceId: canonicalWorkspace.workspaceId,
|
|
5333
|
+
provider, sourceRoot: prepared.sourceRoot, recordBytes: pendingRecord, assertCurrent: prepared.assertCurrent,
|
|
5334
|
+
expected: { organizationId: prepared.config.organizationId, deviceId: prepared.config.deviceId,
|
|
5335
|
+
repositoryAgentId,
|
|
5336
|
+
repositoryBindingId: prepared.workspace.repositoryBindingId ?? null,
|
|
5337
|
+
policyHash: sha256(canonicalize(prepared.policy)), rolloutId: prepared.rollout.id,
|
|
5338
|
+
bundleId: prepared.bundle.bundleId, bundleHash: prepared.bundle.bundleHash },
|
|
5339
|
+
onCommitted: () => { retained = true; } });
|
|
5340
|
+
skillPreparationsCompleted += 1;
|
|
5331
5341
|
}
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
let evidenceRequestId;
|
|
5349
|
-
if (evidencePolicyFresh) {
|
|
5350
|
-
const evidenceCycle = await deferUnavailableRelayRetention(async () => {
|
|
5351
|
-
const synced = await syncPendingRetentionCapsules(vault, fabric, policy, canonicalWorkspace.workspaceId);
|
|
5352
|
-
const evidence = await processEvidenceRequest(fabric, policy, canonicalWorkspace.workspaceId);
|
|
5353
|
-
return {
|
|
5354
|
-
synced,
|
|
5355
|
-
evidenceRequestId: typeof evidence.requestId === 'string' ? evidence.requestId : undefined,
|
|
5356
|
-
};
|
|
5357
|
-
});
|
|
5358
|
-
if (evidenceCycle.state === 'deferred') {
|
|
5359
|
-
evidencePolicyFresh = false;
|
|
5360
|
-
}
|
|
5361
|
-
else {
|
|
5362
|
-
trajectorySyncsCompleted += evidenceCycle.value.synced;
|
|
5363
|
-
evidenceRequestId = evidenceCycle.value.evidenceRequestId;
|
|
5364
|
-
if (evidenceRequestId)
|
|
5365
|
-
evidenceResponsesCompleted += 1;
|
|
5366
|
-
}
|
|
5342
|
+
finally {
|
|
5343
|
+
if (!retained)
|
|
5344
|
+
await rm(prepared.sourceRoot, { recursive: true, force: true });
|
|
5345
|
+
}
|
|
5346
|
+
});
|
|
5347
|
+
}, () => { skillPreparationFailures += 1; });
|
|
5348
|
+
},
|
|
5349
|
+
onError: () => { skillPreparationFailures += 1; },
|
|
5350
|
+
});
|
|
5351
|
+
stopPreparation = skillPreparationPump.requestStop;
|
|
5352
|
+
if (stopping)
|
|
5353
|
+
stopPreparation();
|
|
5354
|
+
try {
|
|
5355
|
+
const recoveredTaskTrajectories = await deferUnavailableRelayRetention(async () => finalizeRecoveredSignedTaskTrajectories(fabric, policy));
|
|
5356
|
+
if (recoveredTaskTrajectories.state === 'completed') {
|
|
5357
|
+
taskTrajectoriesRecovered += recoveredTaskTrajectories.value.length;
|
|
5367
5358
|
}
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
tasksCompleted += 1;
|
|
5371
|
-
if (typeof result.failureCategory === 'string')
|
|
5372
|
-
lastProviderFailureCategory = result.failureCategory;
|
|
5373
|
-
if (!result.taskId && performance.now() >= nextSkillActivationAt) {
|
|
5374
|
-
for (const adapter of providerAdapters) {
|
|
5359
|
+
do {
|
|
5360
|
+
if (Date.now() >= nextPolicyRefreshAt) {
|
|
5375
5361
|
try {
|
|
5376
|
-
|
|
5377
|
-
|
|
5378
|
-
|
|
5379
|
-
const provider = adapter.providerId;
|
|
5380
|
-
await withWorkspaceSkillActivationLock(canonicalWorkspace.workspaceId, provider, async () => {
|
|
5381
|
-
await withSkillPreparationTransaction({ home: dharmaHome(), workspaceId: canonicalWorkspace.workspaceId,
|
|
5382
|
-
provider, assertCurrent: () => { if (stopping)
|
|
5383
|
-
throw new Error('Relay is stopping.'); } }, async () => {
|
|
5384
|
-
const cached = await takeCachedSkillUpdate({ workspaceId: canonicalWorkspace.workspaceId,
|
|
5385
|
-
provider, policyPath, fabric });
|
|
5386
|
-
const prepared = cached || await prepareSkillUpdate({ workspaceId: canonicalWorkspace.workspaceId,
|
|
5387
|
-
provider, policyPath, fabric, automatic: true });
|
|
5388
|
-
if (!prepared)
|
|
5389
|
-
return;
|
|
5390
|
-
if (!cached)
|
|
5391
|
-
skillPreparationsCompleted += 1;
|
|
5392
|
-
await activatePreparedSkillUpdate({ prepared, workspaceId: canonicalWorkspace.workspaceId,
|
|
5393
|
-
provider, policyPath, flags: new Map() });
|
|
5394
|
-
skillActivationsCompleted += 1;
|
|
5395
|
-
});
|
|
5396
|
-
});
|
|
5362
|
+
await syncWorkspacePolicy(fabric, canonicalWorkspace, policy.revision, true);
|
|
5363
|
+
policy = await loadVerifiedWorkspacePolicy(policyPath, canonicalWorkspace.workspaceId);
|
|
5364
|
+
evidencePolicyFresh = true;
|
|
5397
5365
|
}
|
|
5398
5366
|
catch {
|
|
5399
|
-
|
|
5400
|
-
skillActivationFailuresByProvider[adapter.providerId] = (skillActivationFailuresByProvider[adapter.providerId] || 0) + 1;
|
|
5367
|
+
evidencePolicyFresh = false;
|
|
5401
5368
|
}
|
|
5369
|
+
nextPolicyRefreshAt = Date.now() + 60_000;
|
|
5402
5370
|
}
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5371
|
+
if (canonicalWorkspace.repositoryBindingId && canonicalWorkspace.repositoryAgentId) {
|
|
5372
|
+
try {
|
|
5373
|
+
const current = await pollRepositoryCandidate({ transport: fabric,
|
|
5374
|
+
outboxRoot: resolve(dharmaHome(), 'relay', 'repository-candidates'),
|
|
5375
|
+
scope: { organizationId: canonicalWorkspace.organizationId, workspaceId: canonicalWorkspace.workspaceId,
|
|
5376
|
+
repositoryBindingId: canonicalWorkspace.repositoryBindingId,
|
|
5377
|
+
repositoryAgentId: canonicalWorkspace.repositoryAgentId } });
|
|
5378
|
+
if (current && canonicalWorkspace.repositoryPackage) {
|
|
5379
|
+
if (current.candidateId !== canonicalWorkspace.repositoryPackage.candidateId
|
|
5380
|
+
|| current.operationId !== canonicalWorkspace.repositoryPackage.operationId) {
|
|
5381
|
+
throw new Error('Repository candidate status changed identity while polling.');
|
|
5382
|
+
}
|
|
5383
|
+
const baseline = advanceRepositorySourceBaseline(canonicalWorkspace.repositoryPackage, current);
|
|
5384
|
+
canonicalWorkspace = { ...canonicalWorkspace, repositoryPackage: { ...canonicalWorkspace.repositoryPackage,
|
|
5385
|
+
state: current.state, candidateId: current.candidateId, operationId: current.operationId,
|
|
5386
|
+
snapshotHash: current.snapshotHash, releaseId: current.releaseId,
|
|
5387
|
+
...baseline,
|
|
5388
|
+
generation: current.state === 'published' ? Math.max(1, canonicalWorkspace.repositoryPackage.generation) : canonicalWorkspace.repositoryPackage.generation } };
|
|
5389
|
+
publishedLocalSnapshotHash = baseline.publishedLocalSnapshotHash;
|
|
5390
|
+
if (current.state === 'blocked')
|
|
5391
|
+
repositorySourceWatcher.invalidate();
|
|
5392
|
+
await saveWorkspaceRecord(canonicalWorkspace);
|
|
5393
|
+
}
|
|
5409
5394
|
}
|
|
5410
|
-
|
|
5411
|
-
|
|
5395
|
+
catch {
|
|
5396
|
+
repositorySourceFailures += 1;
|
|
5412
5397
|
}
|
|
5413
|
-
|
|
5414
|
-
|
|
5415
|
-
|
|
5416
|
-
|
|
5398
|
+
}
|
|
5399
|
+
let evidenceRequestId;
|
|
5400
|
+
if (evidencePolicyFresh) {
|
|
5401
|
+
const evidenceCycle = await deferUnavailableRelayRetention(async () => {
|
|
5402
|
+
const synced = await syncPendingRetentionCapsules(vault, fabric, policy, canonicalWorkspace.workspaceId);
|
|
5403
|
+
const evidence = await processEvidenceRequest(fabric, policy, canonicalWorkspace.workspaceId);
|
|
5404
|
+
return {
|
|
5405
|
+
synced,
|
|
5406
|
+
evidenceRequestId: typeof evidence.requestId === 'string' ? evidence.requestId : undefined,
|
|
5407
|
+
};
|
|
5408
|
+
});
|
|
5409
|
+
if (evidenceCycle.state === 'deferred') {
|
|
5410
|
+
evidencePolicyFresh = false;
|
|
5417
5411
|
}
|
|
5418
5412
|
else {
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5422
|
-
|
|
5413
|
+
trajectorySyncsCompleted += evidenceCycle.value.synced;
|
|
5414
|
+
evidenceRequestId = evidenceCycle.value.evidenceRequestId;
|
|
5415
|
+
if (evidenceRequestId)
|
|
5416
|
+
evidenceResponsesCompleted += 1;
|
|
5417
|
+
}
|
|
5418
|
+
}
|
|
5419
|
+
const result = await executeOneTask(fabric, leaseSeconds);
|
|
5420
|
+
if (result.taskId)
|
|
5421
|
+
tasksCompleted += 1;
|
|
5422
|
+
if (typeof result.failureCategory === 'string')
|
|
5423
|
+
lastProviderFailureCategory = result.failureCategory;
|
|
5424
|
+
if (!result.taskId && performance.now() >= nextSkillActivationAt) {
|
|
5425
|
+
for (const adapter of providerAdapters) {
|
|
5426
|
+
try {
|
|
5427
|
+
const capability = await adapter.capability();
|
|
5428
|
+
if (capability.skillInstall === 'unavailable' || !isLocalProviderId(adapter.providerId))
|
|
5429
|
+
continue;
|
|
5430
|
+
const provider = adapter.providerId;
|
|
5431
|
+
await withWorkspaceSkillActivationLock(canonicalWorkspace.workspaceId, provider, async () => {
|
|
5432
|
+
await withSkillPreparationTransaction({ home: dharmaHome(), workspaceId: canonicalWorkspace.workspaceId,
|
|
5433
|
+
provider, assertCurrent: () => { if (stopping)
|
|
5434
|
+
throw new Error('Relay is stopping.'); } }, async () => {
|
|
5435
|
+
const cached = await takeCachedSkillUpdate({ workspaceId: canonicalWorkspace.workspaceId,
|
|
5436
|
+
provider, policyPath, fabric });
|
|
5437
|
+
const prepared = cached || await prepareSkillUpdate({ workspaceId: canonicalWorkspace.workspaceId,
|
|
5438
|
+
provider, policyPath, fabric, automatic: true });
|
|
5439
|
+
if (!prepared)
|
|
5440
|
+
return;
|
|
5441
|
+
if (!cached)
|
|
5442
|
+
skillPreparationsCompleted += 1;
|
|
5443
|
+
await activatePreparedSkillUpdate({ prepared, workspaceId: canonicalWorkspace.workspaceId,
|
|
5444
|
+
provider, policyPath, flags: new Map() });
|
|
5445
|
+
skillActivationsCompleted += 1;
|
|
5446
|
+
});
|
|
5447
|
+
});
|
|
5423
5448
|
}
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
}
|
|
5439
|
-
|
|
5440
|
-
if (
|
|
5441
|
-
|
|
5442
|
-
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5447
|
-
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5449
|
+
catch {
|
|
5450
|
+
skillActivationFailures += 1;
|
|
5451
|
+
skillActivationFailuresByProvider[adapter.providerId] = (skillActivationFailuresByProvider[adapter.providerId] || 0) + 1;
|
|
5452
|
+
}
|
|
5453
|
+
}
|
|
5454
|
+
nextSkillActivationAt = performance.now() + 60_000;
|
|
5455
|
+
}
|
|
5456
|
+
if (performance.now() >= nextRepositorySourceScanAt) {
|
|
5457
|
+
try {
|
|
5458
|
+
if (!repositorySourceBaselineAvailable) {
|
|
5459
|
+
throw new Error('Repository source baseline is unavailable; refusing an unanchored update.');
|
|
5460
|
+
}
|
|
5461
|
+
if (!canonicalWorkspace.repositoryBindingId || !canonicalWorkspace.repositoryAgentId) {
|
|
5462
|
+
throw new Error('Repository source synchronization requires a complete repository binding.');
|
|
5463
|
+
}
|
|
5464
|
+
const installedKnowledge = await installedRepositoryKnowledge(canonicalWorkspace);
|
|
5465
|
+
if (!installedKnowledge && (canonicalWorkspace.repositoryPackage?.generation ?? 0) > 0) {
|
|
5466
|
+
repositorySourceState = 'awaiting_signed_baseline';
|
|
5467
|
+
nextRepositorySourceScanAt = performance.now() + 15_000;
|
|
5468
|
+
}
|
|
5469
|
+
else {
|
|
5470
|
+
if (canonicalWorkspace.repositoryPackage?.state === 'blocked' && canonicalWorkspace.repositoryPackage.snapshotHash) {
|
|
5471
|
+
const blocked = await readRepositorySourceBaselineSnapshot(canonicalWorkspace.path, canonicalWorkspace.repositoryPackage.snapshotHash);
|
|
5472
|
+
if (blockedRepositorySourceRetry.consider(canonicalWorkspace.repositoryPackage.candidateId ?? '', blocked.manifest.knowledge, installedKnowledge))
|
|
5473
|
+
repositorySourceWatcher.invalidate();
|
|
5474
|
+
}
|
|
5475
|
+
const sourceCycle = await scanRepositorySourceChanges({
|
|
5476
|
+
workspace: canonicalWorkspace.path, organizationId: canonicalWorkspace.organizationId,
|
|
5477
|
+
workspaceId: canonicalWorkspace.workspaceId, repositoryBindingId: canonicalWorkspace.repositoryBindingId,
|
|
5478
|
+
repositoryAgentId: canonicalWorkspace.repositoryAgentId, transport: fabric, watcher: repositorySourceWatcher,
|
|
5479
|
+
loadRetainedKnowledge: () => installedRepositoryKnowledge(canonicalWorkspace),
|
|
5480
|
+
loadPublishedLocalBaseline: () => publishedLocalSnapshotHash
|
|
5481
|
+
? readRepositoryPackageSnapshot(canonicalWorkspace.path, publishedLocalSnapshotHash) : Promise.resolve(null),
|
|
5482
|
+
submitCandidate: (snapshot, expectedLatestSourceFingerprint) => synchronizeRepositoryCandidate({ transport: fabric,
|
|
5483
|
+
outboxRoot: resolve(dharmaHome(), 'relay', 'repository-candidates'),
|
|
5484
|
+
scope: { organizationId: canonicalWorkspace.organizationId,
|
|
5485
|
+
workspaceId: canonicalWorkspace.workspaceId,
|
|
5486
|
+
repositoryBindingId: canonicalWorkspace.repositoryBindingId,
|
|
5487
|
+
repositoryAgentId: canonicalWorkspace.repositoryAgentId },
|
|
5488
|
+
snapshot, initialRepository: false, expectedLatestSourceFingerprint }),
|
|
5489
|
+
});
|
|
5490
|
+
repositorySourceState = sourceCycle.state;
|
|
5491
|
+
if (sourceCycle.localMutation) {
|
|
5492
|
+
repositorySourceCandidates += 1;
|
|
5493
|
+
if (canonicalWorkspace.repositoryPackage && sourceCycle.persisted) {
|
|
5494
|
+
canonicalWorkspace.repositoryPackage.localBaselineSnapshotHash = sourceCycle.persisted.snapshotHash;
|
|
5495
|
+
if (sourceCycle.state === 'source_already_published') {
|
|
5462
5496
|
canonicalWorkspace.repositoryPackage.publishedLocalSnapshotHash = sourceCycle.persisted.snapshotHash;
|
|
5463
5497
|
canonicalWorkspace.repositoryPackage.pendingLocalSnapshotHash = null;
|
|
5464
5498
|
canonicalWorkspace.repositoryPackage.pendingLocalOperationId = null;
|
|
5465
5499
|
publishedLocalSnapshotHash = sourceCycle.persisted.snapshotHash;
|
|
5466
5500
|
}
|
|
5467
|
-
else if (
|
|
5468
|
-
|
|
5469
|
-
|
|
5470
|
-
|
|
5471
|
-
|
|
5472
|
-
|
|
5473
|
-
|
|
5474
|
-
|
|
5501
|
+
else if (sourceCycle.candidate) {
|
|
5502
|
+
const receipt = sourceCycle.candidate;
|
|
5503
|
+
if (typeof receipt.candidateId === 'string' && typeof receipt.operationId === 'string'
|
|
5504
|
+
&& typeof receipt.snapshotHash === 'string' && sourceCycle.submittedManifestHash) {
|
|
5505
|
+
Object.assign(canonicalWorkspace.repositoryPackage, {
|
|
5506
|
+
state: receipt.state, candidateId: receipt.candidateId, operationId: receipt.operationId,
|
|
5507
|
+
snapshotHash: receipt.snapshotHash, sourceManifestHash: sourceCycle.submittedManifestHash,
|
|
5508
|
+
releaseId: typeof receipt.releaseId === 'string' ? receipt.releaseId : null,
|
|
5509
|
+
consolidationMode: 'repository_update',
|
|
5510
|
+
});
|
|
5511
|
+
}
|
|
5512
|
+
if (receipt.state === 'published') {
|
|
5513
|
+
canonicalWorkspace.repositoryPackage.publishedLocalSnapshotHash = sourceCycle.persisted.snapshotHash;
|
|
5514
|
+
canonicalWorkspace.repositoryPackage.pendingLocalSnapshotHash = null;
|
|
5515
|
+
canonicalWorkspace.repositoryPackage.pendingLocalOperationId = null;
|
|
5516
|
+
publishedLocalSnapshotHash = sourceCycle.persisted.snapshotHash;
|
|
5517
|
+
}
|
|
5518
|
+
else if ((receipt.state === 'accepted' || receipt.state === 'processing')
|
|
5519
|
+
&& typeof receipt.operationId === 'string') {
|
|
5520
|
+
canonicalWorkspace.repositoryPackage.pendingLocalSnapshotHash = sourceCycle.persisted.snapshotHash;
|
|
5521
|
+
canonicalWorkspace.repositoryPackage.pendingLocalOperationId = receipt.operationId;
|
|
5522
|
+
}
|
|
5523
|
+
else if (receipt.state === 'blocked') {
|
|
5524
|
+
canonicalWorkspace.repositoryPackage.localBaselineSnapshotHash = publishedLocalSnapshotHash;
|
|
5525
|
+
repositorySourceWatcher.invalidate();
|
|
5526
|
+
}
|
|
5475
5527
|
}
|
|
5528
|
+
await saveWorkspaceRecord(canonicalWorkspace);
|
|
5476
5529
|
}
|
|
5477
|
-
await saveWorkspaceRecord(canonicalWorkspace);
|
|
5478
5530
|
}
|
|
5531
|
+
nextRepositorySourceScanAt = performance.now() + (sourceCycle.state === 'debouncing' ? 15_000 : 60_000);
|
|
5479
5532
|
}
|
|
5480
|
-
|
|
5533
|
+
}
|
|
5534
|
+
catch {
|
|
5535
|
+
repositorySourceWatcher.invalidate();
|
|
5536
|
+
repositorySourceFailures += 1;
|
|
5537
|
+
repositorySourceState = 'blocked';
|
|
5538
|
+
nextRepositorySourceScanAt = performance.now() + 60_000;
|
|
5481
5539
|
}
|
|
5482
5540
|
}
|
|
5483
|
-
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
}
|
|
5541
|
+
if (flags.has('once'))
|
|
5542
|
+
break;
|
|
5543
|
+
if (!result.taskId && !evidenceRequestId)
|
|
5544
|
+
await new Promise((accept) => setTimeout(accept, pollMs));
|
|
5545
|
+
} while (!stopping);
|
|
5546
|
+
}
|
|
5547
|
+
finally {
|
|
5548
|
+
await skillPreparationPump.stop();
|
|
5549
|
+
vault.close();
|
|
5550
|
+
process.removeListener('SIGINT', stop);
|
|
5551
|
+
process.removeListener('SIGTERM', stop);
|
|
5552
|
+
}
|
|
5553
|
+
return {
|
|
5554
|
+
ok: true, stopped: true, tasksCompleted, taskTrajectoriesRecovered,
|
|
5555
|
+
lastProviderFailureCategory,
|
|
5556
|
+
evidenceResponsesCompleted, trajectorySyncsCompleted,
|
|
5557
|
+
repositorySourceCandidates, repositorySourceFailures, repositorySourceState,
|
|
5558
|
+
skillPreparationsCompleted, skillPreparationFailures, skillActivationsCompleted, skillActivationFailures,
|
|
5559
|
+
skillActivationFailuresByProvider,
|
|
5560
|
+
sharedRepositoryReady: await repositorySharedReady(canonicalWorkspace),
|
|
5561
|
+
};
|
|
5495
5562
|
}
|
|
5496
5563
|
finally {
|
|
5497
|
-
await
|
|
5498
|
-
vault.close();
|
|
5499
|
-
process.removeListener('SIGINT', stop);
|
|
5500
|
-
process.removeListener('SIGTERM', stop);
|
|
5501
|
-
await rm(pidPath, { force: true });
|
|
5564
|
+
await releaseRelayLease();
|
|
5502
5565
|
}
|
|
5503
|
-
return {
|
|
5504
|
-
ok: true, stopped: true, tasksCompleted, taskTrajectoriesRecovered,
|
|
5505
|
-
lastProviderFailureCategory,
|
|
5506
|
-
evidenceResponsesCompleted, trajectorySyncsCompleted,
|
|
5507
|
-
repositorySourceCandidates, repositorySourceFailures, repositorySourceState,
|
|
5508
|
-
skillPreparationsCompleted, skillPreparationFailures, skillActivationsCompleted, skillActivationFailures,
|
|
5509
|
-
skillActivationFailuresByProvider,
|
|
5510
|
-
sharedRepositoryReady: await repositorySharedReady(canonicalWorkspace),
|
|
5511
|
-
};
|
|
5512
5566
|
}
|
|
5513
5567
|
export async function run(argv) {
|
|
5514
5568
|
const { positional, flags, repeated } = parseCliOptions(argv);
|