@ramex-labs/continuity-remote 0.3.0-preview.1
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/BUILD-PROVENANCE.json +204 -0
- package/LICENSE +202 -0
- package/LICENSING.md +7 -0
- package/NOTICE +7 -0
- package/README.md +90 -0
- package/THIRD-PARTY-NOTICES.md +7 -0
- package/dist/remote-tools/attempts.d.mts +1 -0
- package/dist/remote-tools/attempts.mjs +1 -0
- package/dist/remote-tools/client.mjs +36 -0
- package/dist/remote-tools/destination.mjs +180 -0
- package/dist/remote-tools/durable-store.mjs +232 -0
- package/dist/remote-tools/executor.mjs +67 -0
- package/dist/remote-tools/index.d.mts +106 -0
- package/dist/remote-tools/index.mjs +7 -0
- package/dist/remote-tools/langchain.d.mts +7 -0
- package/dist/remote-tools/langchain.mjs +62 -0
- package/dist/remote-tools/local.d.mts +1 -0
- package/dist/remote-tools/local.mjs +1 -0
- package/dist/remote-tools/recovery.mjs +40 -0
- package/dist/remote-tools/runtime.d.mts +1 -0
- package/dist/remote-tools/runtime.mjs +1 -0
- package/dist/remote-tools/strict-json.mjs +136 -0
- package/dist/remote-tools/validation.mjs +96 -0
- package/dist/remote-tools/wire.mjs +150 -0
- package/examples/native-consumer.mjs +44 -0
- package/examples/walkthrough.mjs +136 -0
- package/package.json +66 -0
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import assert from 'node:assert/strict';
|
|
2
|
+
import { createHash, generateKeyPairSync, randomUUID } from 'node:crypto';
|
|
3
|
+
import { mkdtempSync, mkdirSync, realpathSync, rmSync } from 'node:fs';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import { join } from 'node:path';
|
|
6
|
+
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts';
|
|
7
|
+
import {
|
|
8
|
+
createToolRegistry, REFERENCE_TOOLS, createCooperativeDestination,
|
|
9
|
+
createCooperativeClient, createCooperativeExecutor, createCooperativeRecovery,
|
|
10
|
+
} from '@ramex-labs/continuity-remote';
|
|
11
|
+
import { createLocalDomain, createLocalReviewOwner } from '@ramex-labs/continuity-remote/local';
|
|
12
|
+
import { openLocalAttemptRecorder, inspectAttemptHistory } from '@ramex-labs/continuity-remote/attempts';
|
|
13
|
+
|
|
14
|
+
// All effects stay in the supplied synthetic service on 127.0.0.1.
|
|
15
|
+
// Fresh private keys stay in memory; no key or credential is printed or saved.
|
|
16
|
+
const directory = realpathSync(mkdtempSync(join(tmpdir(), 'continuity-walkthrough-')));
|
|
17
|
+
let destination;
|
|
18
|
+
try {
|
|
19
|
+
const now = () => Math.floor(Date.now() / 1000);
|
|
20
|
+
const expiresAt = now() + 3600;
|
|
21
|
+
const coordinator = generateKeyPairSync('ed25519');
|
|
22
|
+
const provider = generateKeyPairSync('ed25519');
|
|
23
|
+
const firstKey = privateKeyToAccount(generatePrivateKey());
|
|
24
|
+
const replacementKey = privateKeyToAccount(generatePrivateKey());
|
|
25
|
+
const serviceId = `synthetic:${randomUUID()}`;
|
|
26
|
+
const registry = createToolRegistry({ serviceId, account: 'example',
|
|
27
|
+
tools: REFERENCE_TOOLS.filter(tool => tool.id === 'ticket.create') });
|
|
28
|
+
const local = { historyFile: join(directory, 'history.jsonl'), domain: createLocalDomain(),
|
|
29
|
+
owner: 'operations', controller: 'example-application', now,
|
|
30
|
+
session: 'session:first', signHash: hash => firstKey.signMessage({ message: { raw: hash } }) };
|
|
31
|
+
|
|
32
|
+
// The application owner creates the role, runtime and narrowly scoped power.
|
|
33
|
+
const owner = createLocalReviewOwner(local); // Explicit E6 review-enabled history.
|
|
34
|
+
owner.createAgent({ id: 'first' });
|
|
35
|
+
owner.createRole({ id: 'operator' });
|
|
36
|
+
owner.appoint({ agent: 'first', role: 'operator', tenure: 'shift:1', number: 1 });
|
|
37
|
+
owner.admitRuntime({ agent: 'first', session: local.session, epoch: 1,
|
|
38
|
+
key: 'key:first', address: firstKey.address, expiresAt });
|
|
39
|
+
owner.grant({ id: 'ticket-power', to: 'first', actions: ['create-ticket'],
|
|
40
|
+
resources: ['queue:security'], expiresAt });
|
|
41
|
+
|
|
42
|
+
const destinationDirectory = join(directory, 'destination');
|
|
43
|
+
mkdirSync(destinationDirectory, { mode: 0o700 });
|
|
44
|
+
destination = await createCooperativeDestination({ directory: destinationDirectory,
|
|
45
|
+
domain: local.domain, serviceId, coordinatorPublicKey: coordinator.publicKey,
|
|
46
|
+
servicePrivateKey: provider.privateKey, now, validateOperation: registry.validateOperation });
|
|
47
|
+
const client = createCooperativeClient({ url: destination.url, serviceId,
|
|
48
|
+
coordinatorPrivateKey: coordinator.privateKey, servicePublicKey: provider.publicKey, timeoutMs: 10000 });
|
|
49
|
+
const dispatchCalls = { prepare: 0, commit: 0 };
|
|
50
|
+
const lostReplyClient = { ...client,
|
|
51
|
+
async prepare(operation) { dispatchCalls.prepare++; return client.prepare(operation); },
|
|
52
|
+
async commit(key) {
|
|
53
|
+
dispatchCalls.commit++;
|
|
54
|
+
await client.commit(key); // The real synthetic destination commits its effect.
|
|
55
|
+
throw new Error('Simulated lost reply after destination commit');
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
const executor = createCooperativeExecutor({ local, client: lostReplyClient, registry, role: 'operator', tenure: 'shift:1' });
|
|
59
|
+
const request = { operationId: 'ticket:1', businessKey: 'case:1', tool: 'ticket.create',
|
|
60
|
+
arguments: { title: 'Review this synthetic incident' } };
|
|
61
|
+
|
|
62
|
+
const first = await executor.run(request);
|
|
63
|
+
assert.equal(first.execution.invocation.status, 'OUTCOME_UNKNOWN');
|
|
64
|
+
assert.equal(first.serviceReport, null);
|
|
65
|
+
assert.equal(destination.inspect().effects.length, 1);
|
|
66
|
+
assert.deepEqual(dispatchCalls, { prepare: 1, commit: 1 });
|
|
67
|
+
console.log('1. The synthetic ticket was created, but its reply was lost: OUTCOME_UNKNOWN.');
|
|
68
|
+
|
|
69
|
+
owner.revoke('ticket-power');
|
|
70
|
+
assert.equal((await executor.checkpoint()).result.state, 'CHECKPOINTED');
|
|
71
|
+
const refused = await executor.run({ ...request, operationId: 'ticket:2', businessKey: 'case:2' });
|
|
72
|
+
assert.equal(refused.execution.status, 'NOT_AUTHORIZED');
|
|
73
|
+
assert.equal(destination.inspect().effects.length, 1);
|
|
74
|
+
console.log('2. Revocation was acknowledged by the destination; a new operation was refused.');
|
|
75
|
+
|
|
76
|
+
// Investigating an old attempt is a separate permission from creating tickets.
|
|
77
|
+
owner.grant({ id: 'first-investigation', to: 'first', actions: ['CREATE_ATTEMPT_DUTY'],
|
|
78
|
+
resources: [request.operationId], expiresAt });
|
|
79
|
+
const firstRecorder = openLocalAttemptRecorder(local);
|
|
80
|
+
await firstRecorder.createDuty({ id: 'investigation', intent: request.operationId,
|
|
81
|
+
description: 'Review the retained report and document what remains unproven', deadline: expiresAt });
|
|
82
|
+
|
|
83
|
+
owner.createAgent({ id: 'replacement' });
|
|
84
|
+
owner.declareSuccession({ id: 'handover-rule', from: 'first', to: 'replacement', role: 'operator' });
|
|
85
|
+
owner.succeed({ id: 'handover', rule: 'handover-rule', fromAgent: 'first', fromTenure: 'shift:1',
|
|
86
|
+
toAgent: 'replacement', toTenure: 'shift:2', role: 'operator', number: 2 });
|
|
87
|
+
owner.admitRuntime({ agent: 'replacement', session: 'session:replacement', epoch: 1,
|
|
88
|
+
key: 'key:replacement', address: replacementKey.address, expiresAt });
|
|
89
|
+
owner.grant({ id: 'replacement-investigation', to: 'replacement',
|
|
90
|
+
actions: ['ASSIGN_ATTEMPT_DUTY', 'OBSERVE_OUTCOME', 'CLOSE_ATTEMPT_DUTY'],
|
|
91
|
+
resources: [request.operationId], expiresAt });
|
|
92
|
+
assert.equal(owner.authorize({ actor: 'replacement', action: 'create-ticket', resource: 'queue:security' }).decision, 'DENY');
|
|
93
|
+
|
|
94
|
+
const replacementLocal = { ...local, session: 'session:replacement',
|
|
95
|
+
signHash: hash => replacementKey.signMessage({ message: { raw: hash } }) };
|
|
96
|
+
const replacement = openLocalAttemptRecorder(replacementLocal);
|
|
97
|
+
await replacement.assignDuty({ id: 'take-investigation', duty: 'investigation' });
|
|
98
|
+
|
|
99
|
+
// After retirement, repeating the old operation only reconciles; it cannot
|
|
100
|
+
// regain execution power or record a new execution acknowledgment.
|
|
101
|
+
const repeated = await executor.run(request);
|
|
102
|
+
assert.equal(repeated.execution.status, 'RECONCILIATION_ONLY');
|
|
103
|
+
assert.equal(repeated.execution.result.status, 'OUTCOME_UNKNOWN');
|
|
104
|
+
assert.deepEqual(dispatchCalls, { prepare: 1, commit: 1 });
|
|
105
|
+
assert.equal(destination.inspect().effects.length, 1);
|
|
106
|
+
|
|
107
|
+
// Recovery asks for authenticated retained status. It never resends the tool.
|
|
108
|
+
const recovery = createCooperativeRecovery({ local: replacementLocal, client, registry });
|
|
109
|
+
const recovered = await recovery.lookup(request);
|
|
110
|
+
assert.equal(recovered.serviceReport.result.state, 'APPLIED');
|
|
111
|
+
assert.equal(recovered.dispatchPerformed, false);
|
|
112
|
+
assert.equal(recovered.externalOutcome, 'NOT_PROVEN');
|
|
113
|
+
const acknowledgment = recovered.observationAcknowledgment;
|
|
114
|
+
assert.equal(acknowledgment.result.kind, 'REMOTE_SERVICE_REPORTED');
|
|
115
|
+
await replacement.observe({ id: 'late-retained-report', intent: request.operationId, acknowledgment });
|
|
116
|
+
const summary = 'Reviewed the retained synthetic service report; business fulfillment remains unproven.';
|
|
117
|
+
const summaryDigest = `0x${createHash('sha256').update(summary).digest('hex')}`;
|
|
118
|
+
await replacement.reviewDuty({ id: 'review:1', duty: 'investigation', summaryDigest });
|
|
119
|
+
|
|
120
|
+
const attempt = inspectAttemptHistory(owner.exportHistory()).attempts.find(item => item.intentId === request.operationId);
|
|
121
|
+
assert.equal(attempt.originalActorId, 'first');
|
|
122
|
+
assert.equal(attempt.observations[0].actorId, 'replacement');
|
|
123
|
+
assert.deepEqual(attempt.observations[0].acknowledgment, acknowledgment);
|
|
124
|
+
assert.equal(attempt.duty.currentAssigneeId, 'replacement');
|
|
125
|
+
assert.equal(attempt.duty.reviewStatus, 'REVIEW_CLOSED');
|
|
126
|
+
assert.equal(attempt.duty.record.status, 'OPEN');
|
|
127
|
+
assert.equal(attempt.externalOutcome, 'NOT_PROVEN');
|
|
128
|
+
assert.equal(destination.inspect().effects.length, 1);
|
|
129
|
+
assert.deepEqual(dispatchCalls, { prepare: 1, commit: 1 });
|
|
130
|
+
console.log('3. After handover, the replacement recovered and recorded the report; no operation was resent.');
|
|
131
|
+
console.log('4. Review: REVIEW_CLOSED. Duty record: OPEN. External outcome: NOT_PROVEN.');
|
|
132
|
+
console.log('All assertions passed; no LLM or real external service was used.');
|
|
133
|
+
} finally {
|
|
134
|
+
try { await destination?.close(); }
|
|
135
|
+
finally { rmSync(directory, { recursive: true, force: true }); }
|
|
136
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ramex-labs/continuity-remote",
|
|
3
|
+
"version": "0.3.0-preview.1",
|
|
4
|
+
"publishConfig": {
|
|
5
|
+
"access": "public",
|
|
6
|
+
"tag": "preview",
|
|
7
|
+
"registry": "https://registry.npmjs.org"
|
|
8
|
+
},
|
|
9
|
+
"repository": {
|
|
10
|
+
"type": "git",
|
|
11
|
+
"url": "https://github.com/zerohourzulu/continuity.git"
|
|
12
|
+
},
|
|
13
|
+
"type": "module",
|
|
14
|
+
"license": "Apache-2.0",
|
|
15
|
+
"description": "Cooperating tool execution, recovery and evidence review; local evaluation profile",
|
|
16
|
+
"engines": {
|
|
17
|
+
"node": "^22.18.0 || ^24.0.0"
|
|
18
|
+
},
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"types": "./dist/remote-tools/index.d.mts",
|
|
22
|
+
"import": "./dist/remote-tools/index.mjs"
|
|
23
|
+
},
|
|
24
|
+
"./langchain": {
|
|
25
|
+
"types": "./dist/remote-tools/langchain.d.mts",
|
|
26
|
+
"import": "./dist/remote-tools/langchain.mjs"
|
|
27
|
+
},
|
|
28
|
+
"./local": {
|
|
29
|
+
"types": "./dist/remote-tools/local.d.mts",
|
|
30
|
+
"import": "./dist/remote-tools/local.mjs"
|
|
31
|
+
},
|
|
32
|
+
"./runtime": {
|
|
33
|
+
"types": "./dist/remote-tools/runtime.d.mts",
|
|
34
|
+
"import": "./dist/remote-tools/runtime.mjs"
|
|
35
|
+
},
|
|
36
|
+
"./attempts": {
|
|
37
|
+
"types": "./dist/remote-tools/attempts.d.mts",
|
|
38
|
+
"import": "./dist/remote-tools/attempts.mjs"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"dependencies": {
|
|
42
|
+
"@ramex-labs/continuity": "0.3.0-preview.7"
|
|
43
|
+
},
|
|
44
|
+
"peerDependencies": {
|
|
45
|
+
"@langchain/core": "1.2.12",
|
|
46
|
+
"zod": "4.6.5"
|
|
47
|
+
},
|
|
48
|
+
"peerDependenciesMeta": {
|
|
49
|
+
"@langchain/core": {
|
|
50
|
+
"optional": true
|
|
51
|
+
},
|
|
52
|
+
"zod": {
|
|
53
|
+
"optional": true
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
"files": [
|
|
57
|
+
"dist",
|
|
58
|
+
"LICENSE",
|
|
59
|
+
"NOTICE",
|
|
60
|
+
"LICENSING.md",
|
|
61
|
+
"THIRD-PARTY-NOTICES.md",
|
|
62
|
+
"README.md",
|
|
63
|
+
"BUILD-PROVENANCE.json",
|
|
64
|
+
"examples"
|
|
65
|
+
]
|
|
66
|
+
}
|