@dharma-ai-labs/agent-fabric 0.2.111 → 0.2.113
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/AGENT_FABRIC_ONBOARDING.md +13 -0
- package/dist/codexBoundSession.d.ts +30 -5
- package/dist/codexBoundSession.d.ts.map +1 -1
- package/dist/codexBoundSession.js +49 -2
- package/dist/codexBoundSession.js.map +1 -1
- package/dist/codexBoundSession.test.js +17 -0
- package/dist/codexBoundSession.test.js.map +1 -1
- package/dist/codexInboxSession.d.ts +50 -0
- package/dist/codexInboxSession.d.ts.map +1 -1
- package/dist/codexInboxSession.js +32 -3
- package/dist/codexInboxSession.js.map +1 -1
- package/dist/codexPeerTools.d.ts +62 -0
- package/dist/codexPeerTools.d.ts.map +1 -0
- package/dist/codexPeerTools.js +53 -0
- package/dist/codexPeerTools.js.map +1 -0
- package/dist/codexPeerTools.test.d.ts +2 -0
- package/dist/codexPeerTools.test.d.ts.map +1 -0
- package/dist/codexPeerTools.test.js +77 -0
- package/dist/codexPeerTools.test.js.map +1 -0
- package/dist/demoPackage.d.ts +32 -0
- package/dist/demoPackage.d.ts.map +1 -1
- package/dist/demoPackage.js +24 -4
- package/dist/demoPackage.js.map +1 -1
- package/dist/demoPackage.test.js +19 -1
- package/dist/demoPackage.test.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +275 -8
- package/dist/index.js.map +1 -1
- package/dist/namedCodexThread.d.ts +3 -0
- package/dist/namedCodexThread.d.ts.map +1 -0
- package/dist/namedCodexThread.js +22 -0
- package/dist/namedCodexThread.js.map +1 -0
- package/dist/namedCodexThread.test.d.ts +2 -0
- package/dist/namedCodexThread.test.d.ts.map +1 -0
- package/dist/namedCodexThread.test.js +63 -0
- package/dist/namedCodexThread.test.js.map +1 -0
- package/dist/namedSessionBudget.d.ts +21 -0
- package/dist/namedSessionBudget.d.ts.map +1 -0
- package/dist/namedSessionBudget.js +70 -0
- package/dist/namedSessionBudget.js.map +1 -0
- package/dist/namedSessionBudget.test.d.ts +2 -0
- package/dist/namedSessionBudget.test.d.ts.map +1 -0
- package/dist/namedSessionBudget.test.js +40 -0
- package/dist/namedSessionBudget.test.js.map +1 -0
- package/dist/namedSessionService.d.ts +39 -0
- package/dist/namedSessionService.d.ts.map +1 -0
- package/dist/namedSessionService.js +269 -0
- package/dist/namedSessionService.js.map +1 -0
- package/dist/namedSessionService.test.d.ts +2 -0
- package/dist/namedSessionService.test.d.ts.map +1 -0
- package/dist/namedSessionService.test.js +158 -0
- package/dist/namedSessionService.test.js.map +1 -0
- package/dist/namedSessionTrust.d.ts +21 -0
- package/dist/namedSessionTrust.d.ts.map +1 -0
- package/dist/namedSessionTrust.js +64 -0
- package/dist/namedSessionTrust.js.map +1 -0
- package/dist/namedSessionTrust.test.d.ts +2 -0
- package/dist/namedSessionTrust.test.d.ts.map +1 -0
- package/dist/namedSessionTrust.test.js +178 -0
- package/dist/namedSessionTrust.test.js.map +1 -0
- package/dist/onboardingStage.d.ts +1 -1
- package/dist/onboardingStage.d.ts.map +1 -1
- package/dist/onboardingStage.js.map +1 -1
- package/dist/providerSessionChannel.d.ts +1 -0
- package/dist/providerSessionChannel.d.ts.map +1 -1
- package/dist/providerSessionChannel.js +5 -1
- package/dist/providerSessionChannel.js.map +1 -1
- package/dist/providerSessionChannel.test.js +10 -3
- package/dist/providerSessionChannel.test.js.map +1 -1
- package/dist/repositoryOnboardingCaller.test.js +19 -0
- package/dist/repositoryOnboardingCaller.test.js.map +1 -1
- package/dist/studyExactTaskSelector.d.ts +3 -0
- package/dist/studyExactTaskSelector.d.ts.map +1 -0
- package/dist/studyExactTaskSelector.js +27 -0
- package/dist/studyExactTaskSelector.js.map +1 -0
- package/dist/studyExactTaskSelector.test.d.ts +2 -0
- package/dist/studyExactTaskSelector.test.d.ts.map +1 -0
- package/dist/studyExactTaskSelector.test.js +25 -0
- package/dist/studyExactTaskSelector.test.js.map +1 -0
- package/dist/usage.d.ts +1 -1
- package/dist/usage.d.ts.map +1 -1
- package/dist/usage.js +1 -1
- package/dist/usage.js.map +1 -1
- package/dist/usage.test.d.ts +2 -0
- package/dist/usage.test.d.ts.map +1 -0
- package/dist/usage.test.js +18 -0
- package/dist/usage.test.js.map +1 -0
- package/package.json +5 -5
package/dist/index.js
CHANGED
|
@@ -18,6 +18,7 @@ import { executeTask, FileActionExecutionJournal, FileTaskReceiptStore, provider
|
|
|
18
18
|
import { CLI_USAGE } from './usage.js';
|
|
19
19
|
import { superviseRelay } from './relaySupervisor.js';
|
|
20
20
|
import { disableRelayAutostart, enableRelayAutostart, relayAutostartStatus, startRelayAutostart } from './relayAutostart.js';
|
|
21
|
+
import { assertStudyExactPoll, studyTaskIdFromFlags } from './studyExactTaskSelector.js';
|
|
21
22
|
import { initializeRepositoryKnowledge, readRepositoryKnowledgeSource } from './repositoryKnowledge.js';
|
|
22
23
|
import { inventoryRepositoryPackage, readRepositoryPackageSnapshot, readRepositorySourceBaselineSnapshot, writeRepositoryPackageSnapshot } from './repositoryPackage.js';
|
|
23
24
|
import { validateRepositorySourceAuthorization } from './repositorySourceAuthorization.js';
|
|
@@ -47,8 +48,12 @@ import { runDemoSupervisor } from './demoSupervisor.js';
|
|
|
47
48
|
import { demoWatchRegistrationKey, listDemoWatchRegistrations } from './demoWatchRegistry.js';
|
|
48
49
|
import { createDemoWatchHealthRecorder } from './demoWatchHealth.js';
|
|
49
50
|
import { demoWatchStatus, disableDemoWatch, enableDemoWatch, inspectDemoWatchSupervisor } from './demoWatchControl.js';
|
|
51
|
+
import { namedSessionPaths, namedSessionRequest, readNamedSession, saveNamedSession, runNamedSessionService } from './namedSessionService.js';
|
|
52
|
+
import { openCodexAppServerTransport } from '@dharma-ai-labs/agent-fabric-provider-adapters/experimental/codex-transport';
|
|
53
|
+
import { createNamedSessionTrust, isNamedSessionOwnerReceipt, renewNamedSessionLifetime } from './namedSessionTrust.js';
|
|
54
|
+
import { startNamedCodexThread } from './namedCodexThread.js';
|
|
50
55
|
export { openCooperativeInboxSession } from './cooperativeInboxSession.js';
|
|
51
|
-
const VERSION = '0.2.
|
|
56
|
+
const VERSION = '0.2.113';
|
|
52
57
|
const USAGE = CLI_USAGE;
|
|
53
58
|
const execFileAsync = promisify(execFile);
|
|
54
59
|
const LOCAL_PROVIDER_IDS = ['codex', 'claude', 'agy', 'hermes'];
|
|
@@ -1667,7 +1672,9 @@ async function relaySupervise(flags) {
|
|
|
1667
1672
|
}),
|
|
1668
1673
|
}) : Promise.resolve({ restarts: 0 });
|
|
1669
1674
|
services.push(standard);
|
|
1670
|
-
const
|
|
1675
|
+
const sessions = policyPath ? superviseNamedSessions(controller.signal) : Promise.resolve();
|
|
1676
|
+
services.push(sessions);
|
|
1677
|
+
const [demoResult, standardResult] = await Promise.all([demo, standard, sessions]);
|
|
1671
1678
|
return { ok: true, stopped: true, ...standardResult, demo: demoResult };
|
|
1672
1679
|
}
|
|
1673
1680
|
finally {
|
|
@@ -1683,6 +1690,42 @@ async function relaySupervise(flags) {
|
|
|
1683
1690
|
await releaseLease();
|
|
1684
1691
|
}
|
|
1685
1692
|
}
|
|
1693
|
+
async function superviseNamedSessions(signal) {
|
|
1694
|
+
if (process.platform !== 'linux')
|
|
1695
|
+
return;
|
|
1696
|
+
while (!signal.aborted) {
|
|
1697
|
+
const root = resolve(dharmaHome(), 'sessions');
|
|
1698
|
+
const entries = await readdir(root, { withFileTypes: true }).catch(error => {
|
|
1699
|
+
if (error.code === 'ENOENT')
|
|
1700
|
+
return [];
|
|
1701
|
+
throw error;
|
|
1702
|
+
});
|
|
1703
|
+
for (const entry of entries.slice(0, 50)) {
|
|
1704
|
+
if (signal.aborted)
|
|
1705
|
+
break;
|
|
1706
|
+
if (!entry.isDirectory() || !/^[a-z][a-z0-9-]{0,47}$/.test(entry.name))
|
|
1707
|
+
continue;
|
|
1708
|
+
try {
|
|
1709
|
+
const registration = await readNamedSession(dharmaHome(), entry.name);
|
|
1710
|
+
if (!registration?.enabled)
|
|
1711
|
+
continue;
|
|
1712
|
+
await namedSessionCommand('start', new Map([
|
|
1713
|
+
['name', registration.name], ['workspace-id', registration.identity.workspaceId], ['apply', true],
|
|
1714
|
+
]));
|
|
1715
|
+
}
|
|
1716
|
+
catch {
|
|
1717
|
+
process.stderr.write(`${JSON.stringify({ event: 'named_session_reconnect_pending', name: entry.name })}\n`);
|
|
1718
|
+
}
|
|
1719
|
+
}
|
|
1720
|
+
await new Promise(resolveWait => {
|
|
1721
|
+
const finish = () => { clearTimeout(timer); signal.removeEventListener('abort', finish); resolveWait(); };
|
|
1722
|
+
const timer = setTimeout(finish, 10_000);
|
|
1723
|
+
signal.addEventListener('abort', finish, { once: true });
|
|
1724
|
+
if (signal.aborted)
|
|
1725
|
+
finish();
|
|
1726
|
+
});
|
|
1727
|
+
}
|
|
1728
|
+
}
|
|
1686
1729
|
async function relayStop() {
|
|
1687
1730
|
const home = dharmaHome();
|
|
1688
1731
|
const supervisor = await relaySupervisorProcessState(home);
|
|
@@ -2035,12 +2078,22 @@ async function bootstrap(flags) {
|
|
|
2035
2078
|
usage,
|
|
2036
2079
|
});
|
|
2037
2080
|
const operatingContract = await loadAgentFabricOnboardingContract();
|
|
2081
|
+
const namedSession = provider === 'codex' && process.platform === 'linux' && sharedRepositoryReady
|
|
2082
|
+
? await withOnboardingStage('named_session', String(onboarded.workspaceId), `dharma bootstrap --resume --complete --portal-url ${hqUrl} --organization-id ${organizationId} --workspace . --policy-revision ${policyRevision}`, () => namedSessionCommand('start', new Map([
|
|
2083
|
+
['name', String(flags.get('session-name') || `codex-${String(onboarded.workspaceId).slice(0, 8)}`)],
|
|
2084
|
+
['workspace-id', String(onboarded.workspaceId)], ['apply', true],
|
|
2085
|
+
...(typeof flags.get('session-budget-cents') === 'string'
|
|
2086
|
+
? [['session-budget-cents', String(flags.get('session-budget-cents'))]] : []),
|
|
2087
|
+
])))
|
|
2088
|
+
: null;
|
|
2038
2089
|
const repositoryReceipt = onboarded;
|
|
2090
|
+
const namedSessionReady = provider !== 'codex' || process.platform !== 'linux'
|
|
2091
|
+
|| Boolean(namedSession?.ok === true && ['running', 'executing'].includes(String(namedSession.state)));
|
|
2039
2092
|
const firstLearning = repositoryReceipt.firstLearningEvidence;
|
|
2040
2093
|
const role = repositoryReceipt.repositoryRole;
|
|
2041
2094
|
return {
|
|
2042
|
-
ok: repositoryReadiness?.outcome !== 'blocked',
|
|
2043
|
-
stage: sharedRepositoryReady ? 'complete'
|
|
2095
|
+
ok: repositoryReadiness?.outcome !== 'blocked' && namedSessionReady,
|
|
2096
|
+
stage: sharedRepositoryReady ? namedSessionReady ? 'complete' : 'named_session_pending'
|
|
2044
2097
|
: repositoryReadiness?.outcome === 'blocked' ? 'shared_repository_blocked' : 'shared_repository_pending',
|
|
2045
2098
|
localStage: 'complete',
|
|
2046
2099
|
sharedRepositoryReady,
|
|
@@ -2065,6 +2118,7 @@ async function bootstrap(flags) {
|
|
|
2065
2118
|
},
|
|
2066
2119
|
relayProbe: relay.probe,
|
|
2067
2120
|
relay,
|
|
2121
|
+
namedSession,
|
|
2068
2122
|
repositoryReadiness,
|
|
2069
2123
|
organizationApi,
|
|
2070
2124
|
operatingContract: {
|
|
@@ -2079,6 +2133,8 @@ async function bootstrap(flags) {
|
|
|
2079
2133
|
? Number(firstLearning.discovered || 0) > 0 ? 'synchronized' : 'no_eligible_history'
|
|
2080
2134
|
: String(firstLearning?.state || 'pending'),
|
|
2081
2135
|
synchronization: relay.state === 'running' ? 'running' : 'pending',
|
|
2136
|
+
namedSession: namedSession && namedSessionReady ? 'ready'
|
|
2137
|
+
: provider === 'codex' && process.platform === 'linux' ? 'pending' : 'not_supported',
|
|
2082
2138
|
},
|
|
2083
2139
|
};
|
|
2084
2140
|
}
|
|
@@ -3161,6 +3217,17 @@ async function repositoryRoleCommand(action, flags) {
|
|
|
3161
3217
|
if (!item)
|
|
3162
3218
|
throw new Error('Repository role workspace is not registered locally.');
|
|
3163
3219
|
const scope = repositoryRoleScope(item), transport = await client();
|
|
3220
|
+
if (['ask', 'reply'].includes(action) && flags.has('session-name')) {
|
|
3221
|
+
const name = required(flags, 'session-name');
|
|
3222
|
+
const registered = await readNamedSession(dharmaHome(), name);
|
|
3223
|
+
if (!registered || registered.identity.workspaceId !== workspaceId)
|
|
3224
|
+
throw new Error('named_session_scope_mismatch');
|
|
3225
|
+
return namedSessionRequest(dharmaHome(), name, action === 'ask' ? {
|
|
3226
|
+
action: 'ask', targetBindingId: required(flags, 'target-session-binding-id'),
|
|
3227
|
+
taskId: required(flags, 'task-id'), category: required(flags, 'category'), question: required(flags, 'question'),
|
|
3228
|
+
} : { action: 'read', questionId: required(flags, 'question-id'),
|
|
3229
|
+
taskId: required(flags, 'task-id'), targetBindingId: required(flags, 'target-session-binding-id') });
|
|
3230
|
+
}
|
|
3164
3231
|
if (action === 'register') {
|
|
3165
3232
|
const categories = required(flags, 'question-categories').split(',').map(value => value.trim()).filter(Boolean);
|
|
3166
3233
|
return registerRepositoryRoleMetadata(transport, scope, { expectedRevision: Number(flags.get('expected-revision') || 0),
|
|
@@ -3177,6 +3244,201 @@ async function repositoryRoleCommand(action, flags) {
|
|
|
3177
3244
|
targetEndpointId: typeof flags.get('target-endpoint-id') === 'string' ? String(flags.get('target-endpoint-id')) : undefined });
|
|
3178
3245
|
return readRepositoryRoleReply({ transport, scope, questionId: required(flags, 'question-id') });
|
|
3179
3246
|
}
|
|
3247
|
+
async function namedSessionCommand(action, flags) {
|
|
3248
|
+
const name = String(flags.get('name') || 'codex');
|
|
3249
|
+
const paths = namedSessionPaths(dharmaHome(), name);
|
|
3250
|
+
if (process.platform !== 'linux')
|
|
3251
|
+
throw new Error('codex_session_sandbox_unqualified');
|
|
3252
|
+
const workspaceId = required(flags, 'workspace-id');
|
|
3253
|
+
const item = (await registry()).find(row => row.workspaceId === workspaceId);
|
|
3254
|
+
const config = await readDeviceConfig();
|
|
3255
|
+
if (!item || !config || item.organizationId !== config.organizationId)
|
|
3256
|
+
throw new Error('named_session_scope_mismatch');
|
|
3257
|
+
const scope = repositoryRoleScope(item);
|
|
3258
|
+
const existing = await readNamedSession(dharmaHome(), name);
|
|
3259
|
+
if (existing && (existing.identity.workspaceId !== item.workspaceId
|
|
3260
|
+
|| existing.identity.organizationId !== config.organizationId || existing.identity.deviceId !== config.deviceId
|
|
3261
|
+
|| existing.identity.endpointId !== scope.endpointId || existing.identity.repositoryBindingId !== scope.repositoryBindingId)) {
|
|
3262
|
+
throw new Error('named_session_scope_mismatch');
|
|
3263
|
+
}
|
|
3264
|
+
if (action === 'status') {
|
|
3265
|
+
try {
|
|
3266
|
+
return await namedSessionRequest(dharmaHome(), name, { action: 'status' });
|
|
3267
|
+
}
|
|
3268
|
+
catch {
|
|
3269
|
+
return { ok: true, name, state: 'stopped', registered: Boolean(existing), enabled: existing?.enabled ?? false,
|
|
3270
|
+
bindingId: existing?.bindingId ?? null };
|
|
3271
|
+
}
|
|
3272
|
+
}
|
|
3273
|
+
if (action === 'work')
|
|
3274
|
+
return namedSessionRequest(dharmaHome(), name, { action: 'work',
|
|
3275
|
+
workId: String(flags.get('work-id') || randomUUID()), prompt: required(flags, 'prompt') });
|
|
3276
|
+
if (action === 'stop') {
|
|
3277
|
+
if (!flags.has('apply'))
|
|
3278
|
+
return { ok: true, planned: true, name, action: 'stop' };
|
|
3279
|
+
if (existing)
|
|
3280
|
+
await saveNamedSession(dharmaHome(), { ...existing, enabled: false });
|
|
3281
|
+
try {
|
|
3282
|
+
return await namedSessionRequest(dharmaHome(), name, { action: 'stop' });
|
|
3283
|
+
}
|
|
3284
|
+
catch {
|
|
3285
|
+
return { ok: true, name, state: 'stopped', enabled: false };
|
|
3286
|
+
}
|
|
3287
|
+
}
|
|
3288
|
+
if (!['start', 'serve'].includes(action))
|
|
3289
|
+
throw new Error('named_session_action_invalid');
|
|
3290
|
+
if (!flags.has('apply'))
|
|
3291
|
+
return { ok: true, planned: true, name, workspaceId,
|
|
3292
|
+
provider: 'codex', localWork: 'workspace_write', peerQuestions: 'read_only', network: 'deny' };
|
|
3293
|
+
if (!await repositorySharedReady(item))
|
|
3294
|
+
throw new Error('named_session_repository_package_pending');
|
|
3295
|
+
if (action === 'start') {
|
|
3296
|
+
if (existing && !existing.enabled)
|
|
3297
|
+
await saveNamedSession(dharmaHome(), { ...existing, enabled: true });
|
|
3298
|
+
try {
|
|
3299
|
+
return await namedSessionRequest(dharmaHome(), name, { action: 'status' });
|
|
3300
|
+
}
|
|
3301
|
+
catch { /* Start an owned worker below. */ }
|
|
3302
|
+
const args = [fileURLToPath(import.meta.url), 'sessions', 'serve', '--name', name,
|
|
3303
|
+
'--workspace-id', workspaceId, '--apply'];
|
|
3304
|
+
for (const key of ['session-budget-cents', 'turn-budget-cents']) {
|
|
3305
|
+
if (flags.has(key))
|
|
3306
|
+
args.push(`--${key}`, required(flags, key));
|
|
3307
|
+
}
|
|
3308
|
+
const child = spawn(process.execPath, args, { cwd: item.path, detached: true, stdio: 'ignore', env: process.env });
|
|
3309
|
+
child.on('error', () => { });
|
|
3310
|
+
child.unref();
|
|
3311
|
+
for (let attempt = 0; attempt < 80; attempt++) {
|
|
3312
|
+
await new Promise(resolveWait => setTimeout(resolveWait, 250));
|
|
3313
|
+
try {
|
|
3314
|
+
return await namedSessionRequest(dharmaHome(), name, { action: 'status' });
|
|
3315
|
+
}
|
|
3316
|
+
catch { /* Await service initialization. */ }
|
|
3317
|
+
}
|
|
3318
|
+
throw new Error('named_session_startup_failed');
|
|
3319
|
+
}
|
|
3320
|
+
await mkdir(paths.root, { recursive: true, mode: 0o700 });
|
|
3321
|
+
const releaseLock = await acquirePidLock(resolve(paths.root, 'service.lock'), 250, 'named_session_already_running');
|
|
3322
|
+
const { LocalVault, loadOrCreateVaultMasterKey } = await loadVaultModule();
|
|
3323
|
+
const vault = await LocalVault.open({ root: resolve(dharmaHome(), 'vault'), masterKey: await loadOrCreateVaultMasterKey() });
|
|
3324
|
+
const controller = new AbortController(), stop = () => controller.abort();
|
|
3325
|
+
process.once('SIGTERM', stop);
|
|
3326
|
+
process.once('SIGINT', stop);
|
|
3327
|
+
let transport;
|
|
3328
|
+
try {
|
|
3329
|
+
const fabric = await AgentFabricClient.open({ configPath: configPath(), statePath: resolve(paths.root, 'protocol-state.json') });
|
|
3330
|
+
if (fabric.config.organizationId !== config.organizationId || fabric.config.deviceId !== config.deviceId
|
|
3331
|
+
|| fabric.config.publicKeyEd25519 !== config.publicKeyEd25519)
|
|
3332
|
+
throw new Error('named_session_trust_scope_mismatch');
|
|
3333
|
+
const trust = createNamedSessionTrust({ configPath: configPath(), identity: fabric.config });
|
|
3334
|
+
await trust.refresh();
|
|
3335
|
+
await fabric.openSession(VERSION);
|
|
3336
|
+
const policyPath = resolve(item.path, '.dharma', 'approved-policy.json');
|
|
3337
|
+
const policy = await refreshVerifiedWorkspacePolicyForTransmission(policyPath, workspaceId, fabric);
|
|
3338
|
+
const writeRoots = policy.tasks.writePaths.filter(path => /^[a-zA-Z0-9_-]+\/\*\*$/.test(path))
|
|
3339
|
+
.map(path => path.slice(0, -3));
|
|
3340
|
+
if (!writeRoots.length)
|
|
3341
|
+
throw new Error('named_session_workspace_write_not_authorized');
|
|
3342
|
+
const workRoots = ['"."="read"', ...writeRoots.map(root => `${JSON.stringify(root)}="write"`)].join(',');
|
|
3343
|
+
transport = await openCodexAppServerTransport({ command: 'codex', cwd: item.path,
|
|
3344
|
+
environment: providerProcessEnvironment(process.env), experimentalApi: true,
|
|
3345
|
+
argv: ['-c', 'default_permissions="dharma_bridge"',
|
|
3346
|
+
'-c', 'permissions.dharma_bridge.filesystem={":minimal"="read",":workspace_roots"={"."="read"}}',
|
|
3347
|
+
'-c', 'permissions.dharma_bridge.network={enabled=false}',
|
|
3348
|
+
'-c', `permissions.dharma_work.filesystem={":minimal"="read",":workspace_roots"={${workRoots}}}`,
|
|
3349
|
+
'-c', 'permissions.dharma_work.network={enabled=false}', 'app-server'] });
|
|
3350
|
+
const account = await transport.request('account/read', { refreshToken: false });
|
|
3351
|
+
if (!account.account)
|
|
3352
|
+
throw new Error('named_session_provider_authentication_required');
|
|
3353
|
+
let registration = existing;
|
|
3354
|
+
if (!registration) {
|
|
3355
|
+
const maximumCostCents = boundedInteger(flags.get('session-budget-cents'), 1000, 1, 10000, '--session-budget-cents');
|
|
3356
|
+
const maximumTurnCostCents = boundedInteger(flags.get('turn-budget-cents'), 25, 1, maximumCostCents, '--turn-budget-cents');
|
|
3357
|
+
const threadId = await startNamedCodexThread(transport, item.path, name);
|
|
3358
|
+
const bindingId = randomUUID();
|
|
3359
|
+
const response = await fabric.signedPost('/agent-fabric/provider-sessions', {
|
|
3360
|
+
schema: 'dharma.provider-session-registration/v1', action: 'attach', provider: 'codex', mode: 'bridge_owned',
|
|
3361
|
+
workspaceId, endpointId: scope.endpointId, repositoryBindingId: scope.repositoryBindingId,
|
|
3362
|
+
bindingId, expectedRevision: 0, leaseSeconds: 60,
|
|
3363
|
+
});
|
|
3364
|
+
const remote = response.registration;
|
|
3365
|
+
if (response.ok !== true || response.organizationId !== config.organizationId || !remote
|
|
3366
|
+
|| remote.bindingId !== bindingId || remote.workspaceId !== workspaceId || remote.endpointId !== scope.endpointId
|
|
3367
|
+
|| remote.repositoryBindingId !== scope.repositoryBindingId || remote.deviceId !== config.deviceId
|
|
3368
|
+
|| !UUID_PATTERN.test(String(remote.membershipId)) || remote.provider !== 'codex'
|
|
3369
|
+
|| remote.mode !== 'bridge_owned' || remote.state !== 'attached' || remote.revision !== 1) {
|
|
3370
|
+
throw new Error('named_session_registration_invalid');
|
|
3371
|
+
}
|
|
3372
|
+
const identity = { organizationId: config.organizationId, repositoryBindingId: scope.repositoryBindingId,
|
|
3373
|
+
workspaceId, endpointId: scope.endpointId, membershipId: String(remote.membershipId), deviceId: config.deviceId,
|
|
3374
|
+
provider: 'codex' };
|
|
3375
|
+
vault.saveProviderSessionBinding({ schema: 'dharma.local-provider-session-binding/v1', ...identity,
|
|
3376
|
+
bindingId, owner: 'dharma_bridge', sessionId: threadId, workspaceRoot: item.path,
|
|
3377
|
+
createdAt: new Date().toISOString(), expiresAt: new Date(Date.now() + 30 * 86400000).toISOString(),
|
|
3378
|
+
maximumProviderCostCents: maximumTurnCostCents });
|
|
3379
|
+
registration = { schema: 'dharma.named-session/v1', name, bindingId, identity,
|
|
3380
|
+
maximumCostCents, maximumTurnCostCents, enabled: true };
|
|
3381
|
+
await saveNamedSession(dharmaHome(), registration);
|
|
3382
|
+
}
|
|
3383
|
+
const authorizeLifetime = async () => {
|
|
3384
|
+
const current = await refreshVerifiedWorkspacePolicyForTransmission(policyPath, workspaceId, fabric);
|
|
3385
|
+
if (!writeRoots.every(root => current.tasks.writePaths.includes(`${root}/**`)))
|
|
3386
|
+
return false;
|
|
3387
|
+
const response = await fabric.signedPost('/agent-fabric/provider-sessions', {
|
|
3388
|
+
schema: 'dharma.provider-session-registration/v1', action: 'inspect', provider: 'codex', mode: 'bridge_owned',
|
|
3389
|
+
workspaceId, endpointId: scope.endpointId, repositoryBindingId: scope.repositoryBindingId,
|
|
3390
|
+
bindingId: registration.bindingId, expectedRevision: 0, leaseSeconds: 60,
|
|
3391
|
+
});
|
|
3392
|
+
return isNamedSessionOwnerReceipt(response, registration.bindingId, registration.identity);
|
|
3393
|
+
};
|
|
3394
|
+
const refreshLifetime = () => renewNamedSessionLifetime({ vault, bindingId: registration.bindingId,
|
|
3395
|
+
identity: registration.identity, trust, authorize: authorizeLifetime });
|
|
3396
|
+
await refreshLifetime();
|
|
3397
|
+
const channelTransport = { async signedPost(route, body) {
|
|
3398
|
+
await trust.refresh();
|
|
3399
|
+
const response = await fabric.signedPost(route, body);
|
|
3400
|
+
await trust.refresh();
|
|
3401
|
+
return response;
|
|
3402
|
+
} };
|
|
3403
|
+
// The durable reservation already suppresses duplicate provider execution.
|
|
3404
|
+
const consumed = new Set();
|
|
3405
|
+
return await runNamedSessionService({ home: dharmaHome(), registration, vault, signal: controller.signal,
|
|
3406
|
+
localWriteRoots: writeRoots,
|
|
3407
|
+
withActivationBoundary: operation => withWorkspaceSkillActivationLock(workspaceId, 'codex', async () => {
|
|
3408
|
+
await refreshLifetime();
|
|
3409
|
+
const skill = await verifyAgentFabricSkillInstallation({ provider: 'codex', workspace: item.path });
|
|
3410
|
+
if (!skill.ready || !await repositorySharedReady(item))
|
|
3411
|
+
throw new Error('named_session_repository_package_pending');
|
|
3412
|
+
return operation();
|
|
3413
|
+
}),
|
|
3414
|
+
authorizeLocalWork: async () => {
|
|
3415
|
+
const current = await refreshVerifiedWorkspacePolicyForTransmission(policyPath, workspaceId, fabric);
|
|
3416
|
+
return writeRoots.every(root => current.tasks.writePaths.includes(`${root}/**`));
|
|
3417
|
+
},
|
|
3418
|
+
openTransport: async () => transport, channelTransport,
|
|
3419
|
+
verifier: { resolvePublicKey: trust.resolvePublicKey, consume: async (id) => { if (consumed.has(id))
|
|
3420
|
+
return false; consumed.add(id); return true; } },
|
|
3421
|
+
authorizeContent: async (content) => {
|
|
3422
|
+
const current = await refreshVerifiedWorkspacePolicyForTransmission(policyPath, workspaceId, fabric);
|
|
3423
|
+
return current.evidence.automaticDisclosure?.mode === 'customer_authorized_content'
|
|
3424
|
+
&& !containsDisallowedLocalPath(content)
|
|
3425
|
+
&& canonicalize(redactValue(content, { classes: new Set(), redactedValues: 0,
|
|
3426
|
+
excludedPaths: 0, inputBytes: 0, outputBytes: 0 })) === canonicalize(content);
|
|
3427
|
+
} });
|
|
3428
|
+
}
|
|
3429
|
+
finally {
|
|
3430
|
+
try {
|
|
3431
|
+
if (transport)
|
|
3432
|
+
await transport.close();
|
|
3433
|
+
}
|
|
3434
|
+
finally {
|
|
3435
|
+
vault.close();
|
|
3436
|
+
await releaseLock();
|
|
3437
|
+
process.removeListener('SIGTERM', stop);
|
|
3438
|
+
process.removeListener('SIGINT', stop);
|
|
3439
|
+
}
|
|
3440
|
+
}
|
|
3441
|
+
}
|
|
3180
3442
|
async function workspaceSync(flags, positional) {
|
|
3181
3443
|
const workspaceId = positional[0] || required(flags, 'workspace-id');
|
|
3182
3444
|
const policyRevision = required(flags, 'policy-revision');
|
|
@@ -4246,8 +4508,10 @@ async function finalizeRecoveredSignedTaskTrajectories(fabric, vaultPolicy, only
|
|
|
4246
4508
|
}
|
|
4247
4509
|
return finalized;
|
|
4248
4510
|
}
|
|
4249
|
-
async function executeOneTask(fabric, leaseSeconds) {
|
|
4250
|
-
const polled = await fabric.pollTask(leaseSeconds);
|
|
4511
|
+
async function executeOneTask(fabric, leaseSeconds, exactStudyTask) {
|
|
4512
|
+
const polled = await fabric.pollTask(leaseSeconds, exactStudyTask?.taskId);
|
|
4513
|
+
if (exactStudyTask)
|
|
4514
|
+
assertStudyExactPoll(polled, exactStudyTask.taskId, exactStudyTask.workspaceId);
|
|
4251
4515
|
const taskRow = polled.task;
|
|
4252
4516
|
if (!taskRow?.envelope)
|
|
4253
4517
|
return { ok: true, task: null };
|
|
@@ -4384,10 +4648,11 @@ async function runOneTask(flags) {
|
|
|
4384
4648
|
&& (!requestedWorkspaceId || item.workspaceId === requestedWorkspaceId)));
|
|
4385
4649
|
if (!selectedWorkspace)
|
|
4386
4650
|
throw new Error('Task policy must be the canonical policy of one registered workspace.');
|
|
4651
|
+
const studyTaskId = studyTaskIdFromFlags(flags, selectedWorkspace.workspaceId);
|
|
4387
4652
|
const policy = await loadVerifiedWorkspacePolicy(policyPath, selectedWorkspace.workspaceId);
|
|
4388
4653
|
const fabric = await client();
|
|
4389
|
-
const recoveredTaskTrajectories = await finalizeRecoveredSignedTaskTrajectories(fabric, policy);
|
|
4390
|
-
const result = await executeOneTask(fabric, Number(flags.get('lease-seconds') || 120));
|
|
4654
|
+
const recoveredTaskTrajectories = studyTaskId ? [] : await finalizeRecoveredSignedTaskTrajectories(fabric, policy);
|
|
4655
|
+
const result = await executeOneTask(fabric, Number(flags.get('lease-seconds') || 120), studyTaskId ? { taskId: studyTaskId, workspaceId: selectedWorkspace.workspaceId } : undefined);
|
|
4391
4656
|
return recoveredTaskTrajectories.length ? { ...result, recoveredTaskTrajectories } : result;
|
|
4392
4657
|
}
|
|
4393
4658
|
export function nativeSkillDirectory(provider, env = process.env, home = homedir()) {
|
|
@@ -6064,6 +6329,8 @@ export async function run(argv) {
|
|
|
6064
6329
|
}
|
|
6065
6330
|
if (command === 'bootstrap')
|
|
6066
6331
|
return bootstrap(flags);
|
|
6332
|
+
if (command === 'sessions')
|
|
6333
|
+
return namedSessionCommand(String(subcommand), flags);
|
|
6067
6334
|
if (command === 'onboard')
|
|
6068
6335
|
return onboard(flags);
|
|
6069
6336
|
if (command === 'login')
|