@relaycast/engine 8.1.2 → 8.2.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/auth/index.js +3 -3
- package/dist/auth/index.js.map +1 -1
- package/dist/db/migrations/0039_workspace_usage_attribution.sql +24 -0
- package/dist/db/migrations/0041_agent_identity_recovery.sql +40 -0
- package/dist/db/migrations/0041_d1_hot_path_indexes.sql +12 -0
- package/dist/db/schema.d.ts +515 -0
- package/dist/db/schema.d.ts.map +1 -1
- package/dist/db/schema.js +73 -1
- package/dist/db/schema.js.map +1 -1
- package/dist/engine/agent.d.ts +3 -1
- package/dist/engine/agent.d.ts.map +1 -1
- package/dist/engine/agent.js +37 -1
- package/dist/engine/agent.js.map +1 -1
- package/dist/engine/agentIdentity.d.ts +49 -0
- package/dist/engine/agentIdentity.d.ts.map +1 -0
- package/dist/engine/agentIdentity.js +158 -0
- package/dist/engine/agentIdentity.js.map +1 -0
- package/dist/engine/node.d.ts +9 -1
- package/dist/engine/node.d.ts.map +1 -1
- package/dist/engine/node.js +122 -54
- package/dist/engine/node.js.map +1 -1
- package/dist/engine/tokenRotate.d.ts +3 -4
- package/dist/engine/tokenRotate.d.ts.map +1 -1
- package/dist/engine/tokenRotate.js +4 -5
- package/dist/engine/tokenRotate.js.map +1 -1
- package/dist/engine/workspace.d.ts +36 -0
- package/dist/engine/workspace.d.ts.map +1 -1
- package/dist/engine/workspace.js +17 -1
- package/dist/engine/workspace.js.map +1 -1
- package/dist/lib/workspaceProvenance.d.ts +17 -0
- package/dist/lib/workspaceProvenance.d.ts.map +1 -0
- package/dist/lib/workspaceProvenance.js +41 -0
- package/dist/lib/workspaceProvenance.js.map +1 -0
- package/dist/routes/agent.d.ts.map +1 -1
- package/dist/routes/agent.js +223 -2
- package/dist/routes/agent.js.map +1 -1
- package/dist/routes/workspace.d.ts.map +1 -1
- package/dist/routes/workspace.js +28 -5
- package/dist/routes/workspace.js.map +1 -1
- package/package.json +3 -3
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import type { getDb } from '../db/index.js';
|
|
2
|
+
type Db = ReturnType<typeof getDb>;
|
|
3
|
+
export type AgentIdentityAuthority = 'current_agent_token' | 'origin_node' | 'work_unit_proof' | 'workspace_admin';
|
|
4
|
+
export interface AgentIdentityTarget {
|
|
5
|
+
workspaceId: string;
|
|
6
|
+
agentId: string;
|
|
7
|
+
agentName: string;
|
|
8
|
+
}
|
|
9
|
+
export interface AgentIdentityAuditContext {
|
|
10
|
+
authority: AgentIdentityAuthority;
|
|
11
|
+
actor: string;
|
|
12
|
+
reason: string;
|
|
13
|
+
sessionRef?: string | null;
|
|
14
|
+
nodeId?: string | null;
|
|
15
|
+
originActor: string;
|
|
16
|
+
}
|
|
17
|
+
export declare function getRecoveryCredentialByProof(db: Db, rawProof: string): Promise<{
|
|
18
|
+
workspaceId: string;
|
|
19
|
+
agentId: string;
|
|
20
|
+
agentName: string;
|
|
21
|
+
workUnitId: string | null;
|
|
22
|
+
} | null>;
|
|
23
|
+
export declare function enrollRecoveryCredential(db: Db, target: Pick<AgentIdentityTarget, 'workspaceId' | 'agentId'>, input: {
|
|
24
|
+
verifierHash: string;
|
|
25
|
+
proofKind?: 'work_unit';
|
|
26
|
+
workUnitId?: string | null;
|
|
27
|
+
}): Promise<void>;
|
|
28
|
+
export declare function rotateAgentIdentity(db: Db, target: AgentIdentityTarget, audit: AgentIdentityAuditContext, action?: 'recover' | 'takeover', options?: {
|
|
29
|
+
requireAtomic?: boolean;
|
|
30
|
+
alreadyAtomic?: boolean;
|
|
31
|
+
}): Promise<{
|
|
32
|
+
agent_id: string;
|
|
33
|
+
name: string;
|
|
34
|
+
token: string;
|
|
35
|
+
audit_id: string;
|
|
36
|
+
}>;
|
|
37
|
+
export declare function takeOverAgentIdentity(db: Db, target: AgentIdentityTarget, audit: Omit<AgentIdentityAuditContext, 'authority'>): Promise<{
|
|
38
|
+
agent_id: string;
|
|
39
|
+
name: string;
|
|
40
|
+
token: string;
|
|
41
|
+
audit_id: string;
|
|
42
|
+
}>;
|
|
43
|
+
export declare function revokeAgentIdentityTokens(db: Db, target: AgentIdentityTarget, audit: Omit<AgentIdentityAuditContext, 'authority'>): Promise<{
|
|
44
|
+
agent_id: string;
|
|
45
|
+
name: string;
|
|
46
|
+
audit_id: string;
|
|
47
|
+
}>;
|
|
48
|
+
export {};
|
|
49
|
+
//# sourceMappingURL=agentIdentity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentIdentity.d.ts","sourceRoot":"","sources":["../../src/engine/agentIdentity.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAS5C,KAAK,EAAE,GAAG,UAAU,CAAC,OAAO,KAAK,CAAC,CAAC;AAEnC,MAAM,MAAM,sBAAsB,GAC9B,qBAAqB,GACrB,aAAa,GACb,iBAAiB,GACjB,iBAAiB,CAAC;AAEtB,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,yBAAyB;IACxC,SAAS,EAAE,sBAAsB,CAAC;IAClC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;CACrB;AA6CD,wBAAsB,4BAA4B,CAChD,EAAE,EAAE,EAAE,EACN,QAAQ,EAAE,MAAM,GACf,OAAO,CAAC;IACT,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B,GAAG,IAAI,CAAC,CAgBR;AAED,wBAAsB,wBAAwB,CAC5C,EAAE,EAAE,EAAE,EACN,MAAM,EAAE,IAAI,CAAC,mBAAmB,EAAE,aAAa,GAAG,SAAS,CAAC,EAC5D,KAAK,EAAE;IACL,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,WAAW,CAAC;IACxB,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;CAC5B,GACA,OAAO,CAAC,IAAI,CAAC,CAuBf;AAED,wBAAsB,mBAAmB,CACvC,EAAE,EAAE,EAAE,EACN,MAAM,EAAE,mBAAmB,EAC3B,KAAK,EAAE,yBAAyB,EAChC,MAAM,GAAE,SAAS,GAAG,UAAsB,EAC1C,OAAO,GAAE;IAAE,aAAa,CAAC,EAAE,OAAO,CAAC;IAAC,aAAa,CAAC,EAAE,OAAO,CAAA;CAAO,GACjE,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAiD9E;AAED,wBAAsB,qBAAqB,CACzC,EAAE,EAAE,EAAE,EACN,MAAM,EAAE,mBAAmB,EAC3B,KAAK,EAAE,IAAI,CAAC,yBAAyB,EAAE,WAAW,CAAC,GAClD,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAK9E;AAED,wBAAsB,yBAAyB,CAC7C,EAAE,EAAE,EAAE,EACN,MAAM,EAAE,mBAAmB,EAC3B,KAAK,EAAE,IAAI,CAAC,yBAAyB,EAAE,WAAW,CAAC,GAClD,OAAO,CAAC;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC,CAoC/D"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { and, eq, ne, sql } from 'drizzle-orm';
|
|
2
|
+
import { agentIdentityAudit, agentRecoveryCredentials, agents } from '../db/schema.js';
|
|
3
|
+
import { randomHex, sha256Hex } from '../lib/crypto.js';
|
|
4
|
+
import { codedError } from '../lib/httpError.js';
|
|
5
|
+
import { runAtomicWrites } from '../ports/database.js';
|
|
6
|
+
import { generateId } from './snowflake.js';
|
|
7
|
+
import { AGENT_TOKEN_GRACE_MS } from './tokenRotate.js';
|
|
8
|
+
import { RELEASED_AGENT_STATUS } from './agent.js';
|
|
9
|
+
function targetPredicate(target) {
|
|
10
|
+
return and(eq(agents.workspaceId, target.workspaceId), eq(agents.id, target.agentId), eq(agents.name, target.agentName), ne(agents.status, RELEASED_AGENT_STATUS));
|
|
11
|
+
}
|
|
12
|
+
function auditInsertAfterTokenChange(writeDb, target, tokenHash, row) {
|
|
13
|
+
// The SELECT sees the token update earlier in the same transaction/batch.
|
|
14
|
+
// If the target predicate matched no live agent, it produces no row, so D1
|
|
15
|
+
// cannot commit a successful-looking audit entry for a failed mutation.
|
|
16
|
+
return writeDb.insert(agentIdentityAudit).select(writeDb
|
|
17
|
+
.select({
|
|
18
|
+
id: sql `${row.id}`.as('id'),
|
|
19
|
+
workspaceId: sql `${row.workspaceId}`.as('workspace_id'),
|
|
20
|
+
agentId: sql `${row.agentId}`.as('agent_id'),
|
|
21
|
+
agentName: sql `${row.agentName}`.as('agent_name'),
|
|
22
|
+
action: sql `${row.action}`.as('action'),
|
|
23
|
+
authority: sql `${row.authority}`.as('authority'),
|
|
24
|
+
actor: sql `${row.actor}`.as('actor'),
|
|
25
|
+
reason: sql `${row.reason}`.as('reason'),
|
|
26
|
+
sessionRef: sql `${row.sessionRef ?? null}`.as('session_ref'),
|
|
27
|
+
nodeId: sql `${row.nodeId ?? null}`.as('node_id'),
|
|
28
|
+
originActor: sql `${row.originActor}`.as('origin_actor'),
|
|
29
|
+
createdAt: sql `unixepoch()`.as('created_at'),
|
|
30
|
+
})
|
|
31
|
+
.from(agents)
|
|
32
|
+
.where(and(targetPredicate(target), eq(agents.tokenHash, tokenHash))));
|
|
33
|
+
}
|
|
34
|
+
function firstReturnedRow(result) {
|
|
35
|
+
return Array.isArray(result) ? result[0] : undefined;
|
|
36
|
+
}
|
|
37
|
+
export async function getRecoveryCredentialByProof(db, rawProof) {
|
|
38
|
+
const verifierHash = await sha256Hex(rawProof);
|
|
39
|
+
const [row] = await db
|
|
40
|
+
.select({
|
|
41
|
+
workspaceId: agentRecoveryCredentials.workspaceId,
|
|
42
|
+
agentId: agentRecoveryCredentials.agentId,
|
|
43
|
+
agentName: agents.name,
|
|
44
|
+
workUnitId: agentRecoveryCredentials.workUnitId,
|
|
45
|
+
})
|
|
46
|
+
.from(agentRecoveryCredentials)
|
|
47
|
+
.innerJoin(agents, and(eq(agents.workspaceId, agentRecoveryCredentials.workspaceId), eq(agents.id, agentRecoveryCredentials.agentId)))
|
|
48
|
+
.where(eq(agentRecoveryCredentials.verifierHash, verifierHash));
|
|
49
|
+
return row ?? null;
|
|
50
|
+
}
|
|
51
|
+
export async function enrollRecoveryCredential(db, target, input) {
|
|
52
|
+
const now = new Date();
|
|
53
|
+
await db
|
|
54
|
+
.insert(agentRecoveryCredentials)
|
|
55
|
+
.values({
|
|
56
|
+
id: `arc_${generateId()}`,
|
|
57
|
+
workspaceId: target.workspaceId,
|
|
58
|
+
agentId: target.agentId,
|
|
59
|
+
proofKind: input.proofKind ?? 'work_unit',
|
|
60
|
+
verifierHash: input.verifierHash,
|
|
61
|
+
workUnitId: input.workUnitId ?? null,
|
|
62
|
+
createdAt: now,
|
|
63
|
+
updatedAt: now,
|
|
64
|
+
})
|
|
65
|
+
.onConflictDoUpdate({
|
|
66
|
+
target: [agentRecoveryCredentials.workspaceId, agentRecoveryCredentials.agentId],
|
|
67
|
+
set: {
|
|
68
|
+
proofKind: input.proofKind ?? 'work_unit',
|
|
69
|
+
verifierHash: input.verifierHash,
|
|
70
|
+
workUnitId: input.workUnitId ?? null,
|
|
71
|
+
updatedAt: now,
|
|
72
|
+
},
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
export async function rotateAgentIdentity(db, target, audit, action = 'recover', options = {}) {
|
|
76
|
+
const token = `at_live_${randomHex(16)}`;
|
|
77
|
+
const tokenHash = await sha256Hex(token);
|
|
78
|
+
const auditId = `aid_${generateId()}`;
|
|
79
|
+
const graceExpiresAtSeconds = Math.floor((Date.now() + AGENT_TOKEN_GRACE_MS) / 1000);
|
|
80
|
+
const buildWrites = (writeDb) => [
|
|
81
|
+
writeDb
|
|
82
|
+
.update(agents)
|
|
83
|
+
.set({
|
|
84
|
+
previousTokenHash: sql `${agents.tokenHash}`,
|
|
85
|
+
previousTokenExpiresAt: sql `${graceExpiresAtSeconds}`,
|
|
86
|
+
tokenHash,
|
|
87
|
+
})
|
|
88
|
+
.where(targetPredicate(target))
|
|
89
|
+
.returning({ id: agents.id }),
|
|
90
|
+
auditInsertAfterTokenChange(writeDb, target, tokenHash, {
|
|
91
|
+
id: auditId,
|
|
92
|
+
workspaceId: target.workspaceId,
|
|
93
|
+
agentId: target.agentId,
|
|
94
|
+
agentName: target.agentName,
|
|
95
|
+
action,
|
|
96
|
+
authority: audit.authority,
|
|
97
|
+
actor: audit.actor,
|
|
98
|
+
reason: audit.reason,
|
|
99
|
+
sessionRef: audit.sessionRef ?? null,
|
|
100
|
+
nodeId: audit.nodeId ?? null,
|
|
101
|
+
originActor: audit.originActor,
|
|
102
|
+
}),
|
|
103
|
+
];
|
|
104
|
+
let results;
|
|
105
|
+
if (options.alreadyAtomic) {
|
|
106
|
+
results = [];
|
|
107
|
+
for (const write of buildWrites(db))
|
|
108
|
+
results.push(await write);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
results = await runAtomicWrites(db, buildWrites, {
|
|
112
|
+
requireAtomic: options.requireAtomic ?? true,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
if (!firstReturnedRow(results[0])) {
|
|
116
|
+
throw codedError(`Agent "${target.agentName}" changed before identity recovery completed`, 'agent_identity_conflict', 409);
|
|
117
|
+
}
|
|
118
|
+
return { agent_id: target.agentId, name: target.agentName, token, audit_id: auditId };
|
|
119
|
+
}
|
|
120
|
+
export async function takeOverAgentIdentity(db, target, audit) {
|
|
121
|
+
return rotateAgentIdentity(db, target, {
|
|
122
|
+
...audit,
|
|
123
|
+
authority: 'workspace_admin',
|
|
124
|
+
}, 'takeover');
|
|
125
|
+
}
|
|
126
|
+
export async function revokeAgentIdentityTokens(db, target, audit) {
|
|
127
|
+
const replacementHash = await sha256Hex(`revoked:${target.agentId}:${randomHex(16)}`);
|
|
128
|
+
const auditId = `aid_${generateId()}`;
|
|
129
|
+
const results = await runAtomicWrites(db, (writeDb) => [
|
|
130
|
+
writeDb
|
|
131
|
+
.update(agents)
|
|
132
|
+
.set({
|
|
133
|
+
tokenHash: replacementHash,
|
|
134
|
+
previousTokenHash: null,
|
|
135
|
+
previousTokenExpiresAt: null,
|
|
136
|
+
})
|
|
137
|
+
.where(targetPredicate(target))
|
|
138
|
+
.returning({ id: agents.id }),
|
|
139
|
+
auditInsertAfterTokenChange(writeDb, target, replacementHash, {
|
|
140
|
+
id: auditId,
|
|
141
|
+
workspaceId: target.workspaceId,
|
|
142
|
+
agentId: target.agentId,
|
|
143
|
+
agentName: target.agentName,
|
|
144
|
+
action: 'revoke',
|
|
145
|
+
authority: 'workspace_admin',
|
|
146
|
+
actor: audit.actor,
|
|
147
|
+
reason: audit.reason,
|
|
148
|
+
sessionRef: audit.sessionRef ?? null,
|
|
149
|
+
nodeId: audit.nodeId ?? null,
|
|
150
|
+
originActor: audit.originActor,
|
|
151
|
+
}),
|
|
152
|
+
], { requireAtomic: true });
|
|
153
|
+
if (!firstReturnedRow(results[0])) {
|
|
154
|
+
throw codedError(`Agent "${target.agentName}" changed before revocation completed`, 'agent_identity_conflict', 409);
|
|
155
|
+
}
|
|
156
|
+
return { agent_id: target.agentId, name: target.agentName, audit_id: auditId };
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=agentIdentity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agentIdentity.js","sourceRoot":"","sources":["../../src/engine/agentIdentity.ts"],"names":[],"mappings":"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"}
|
package/dist/engine/node.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { FleetAgentRegisterMessage, FleetCapabilityAcceptance, FleetInventoryAgent, FleetNodeHeartbeatMessage, FleetNodeRegisterMessage, FleetProviderIdentity, FleetCapability, AgentRegisterReplyData } from '@relaycast/types';
|
|
1
|
+
import type { FleetAgentRecoverMessage, FleetAgentRegisterMessage, FleetCapabilityAcceptance, FleetInventoryAgent, FleetNodeHeartbeatMessage, FleetNodeRegisterMessage, FleetProviderIdentity, FleetCapability, AgentRegisterReplyData } from '@relaycast/types';
|
|
2
2
|
import type { getDb } from '../db/index.js';
|
|
3
3
|
import { agents, nodes } from '../db/schema.js';
|
|
4
4
|
import type { EngineDb } from '../ports/database.js';
|
|
@@ -328,6 +328,14 @@ export declare function listNodeAgents(db: Db, workspaceId: string, nodeName: st
|
|
|
328
328
|
export declare function registerAgentViaNode(db: Db, workspaceId: string, nodeId: string, providerName: string, message: FleetAgentRegisterMessage, options?: {
|
|
329
329
|
deliveryCursorSupported?: boolean;
|
|
330
330
|
}): Promise<AgentRegisterReplyData>;
|
|
331
|
+
/**
|
|
332
|
+
* Recover an existing agent using the authenticated node as the authority.
|
|
333
|
+
* The immutable id and server-owned `origin_node_id` must both match; name,
|
|
334
|
+
* presence, silence, and the synthetic direct-node convention grant nothing.
|
|
335
|
+
*/
|
|
336
|
+
export declare function recoverAgentViaNode(db: Db, workspaceId: string, nodeId: string, providerName: string, message: FleetAgentRecoverMessage, options?: {
|
|
337
|
+
deliveryCursorSupported?: boolean;
|
|
338
|
+
}): Promise<AgentRegisterReplyData>;
|
|
331
339
|
export declare function deregisterAgentViaNode(db: Db, workspaceId: string, nodeId: string, message: {
|
|
332
340
|
agent_id?: string;
|
|
333
341
|
name?: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../src/engine/node.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,yBAAyB,EAEzB,yBAAyB,EACzB,mBAAmB,EACnB,yBAAyB,EACzB,wBAAwB,EACxB,qBAAqB,EACrB,eAAe,EACf,sBAAsB,EACvB,MAAM,kBAAkB,CAAC;AAK1B,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAC;AAC5C,OAAO,EAAqB,MAAM,EAA8D,KAAK,EAAE,MAAM,iBAAiB,CAAC;AAI/H,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAgC,KAAK,sBAAsB,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"node.d.ts","sourceRoot":"","sources":["../../src/engine/node.ts"],"names":[],"mappings":"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"}
|
package/dist/engine/node.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { and, eq, inArray,
|
|
1
|
+
import { and, eq, inArray, ne, or, sql } from 'drizzle-orm';
|
|
2
2
|
import { FLEET_DELIVERY_CURSOR_CAPABILITY, parseFleetBrokerToRelaycastMessage, } from '@relaycast/types';
|
|
3
3
|
import { actionInvocations, agents, agentNodeBindings, channelMembers, channels, nodeProviders, nodes } from '../db/schema.js';
|
|
4
4
|
import { randomHex, sha256Hex } from '../lib/crypto.js';
|
|
@@ -6,7 +6,8 @@ import { codedError } from '../lib/httpError.js';
|
|
|
6
6
|
import { runAtomic } from '../ports/database.js';
|
|
7
7
|
import { isProviderAgentDeliveryReady } from '../ports/realtime.js';
|
|
8
8
|
import { generateId } from './snowflake.js';
|
|
9
|
-
import {
|
|
9
|
+
import { assertRegistrableAgentName } from './agent.js';
|
|
10
|
+
import { rotateAgentIdentity } from './agentIdentity.js';
|
|
10
11
|
import { isNodeLive, nodeHasCapability } from './placement.js';
|
|
11
12
|
import { DEFAULT_PROVIDER_NAME, capabilityKind, heartbeatProvider, markProviderOffline, materializeProviderActions, recomputeNodeAggregate, removeProvider, upsertProvider, } from './nodeProvider.js';
|
|
12
13
|
import { serializeNodeOp } from './nodeLock.js';
|
|
@@ -914,11 +915,6 @@ export async function registerAgentViaNode(db, workspaceId, nodeId, providerName
|
|
|
914
915
|
const token = `at_live_${randomHex(16)}`;
|
|
915
916
|
const tokenHash = await sha256Hex(token);
|
|
916
917
|
const now = new Date().toISOString();
|
|
917
|
-
const fleetMetadata = sql `json_object(
|
|
918
|
-
'node_id', ${nodeId},
|
|
919
|
-
'invocation_id', ${message.invocation_id ?? null},
|
|
920
|
-
'registered_at', ${now}
|
|
921
|
-
)`;
|
|
922
918
|
const [result] = await tx
|
|
923
919
|
.insert(agents)
|
|
924
920
|
.values({
|
|
@@ -938,55 +934,10 @@ export async function registerAgentViaNode(db, workspaceId, nodeId, providerName
|
|
|
938
934
|
resumable: message.resumable ?? false,
|
|
939
935
|
sessionRef: message.session_ref ?? null,
|
|
940
936
|
})
|
|
941
|
-
.
|
|
942
|
-
target: [agents.workspaceId, agents.name],
|
|
943
|
-
set: {
|
|
944
|
-
tokenHash,
|
|
945
|
-
status: 'active',
|
|
946
|
-
lastSeen: new Date(),
|
|
947
|
-
metadata: sql `json_patch(COALESCE(${agents.metadata}, '{}'), ${fleetMetadata})`,
|
|
948
|
-
locationType: 'via_node',
|
|
949
|
-
locationNodeId: nodeId,
|
|
950
|
-
providerName,
|
|
951
|
-
originNodeId: sql `COALESCE(${agents.originNodeId}, ${nodeId})`,
|
|
952
|
-
resumable: message.resumable ?? false,
|
|
953
|
-
sessionRef: message.session_ref ?? null,
|
|
954
|
-
},
|
|
955
|
-
// Who may take this name and be issued a token for it.
|
|
956
|
-
//
|
|
957
|
-
// The first disjunct gates on OBSERVED SILENCE (`last_seen`), not on
|
|
958
|
-
// the `status` column. Those are not the same question and must not
|
|
959
|
-
// share a field. `status` is maintained by `sweepStaleAgents`, which
|
|
960
|
-
// runs on every roster read — so while identity was gated on it, an
|
|
961
|
-
// `agent list` flipped records to 'offline' and thereby moved them
|
|
962
|
-
// from "reclaimable only by their own node" to "reclaimable by any
|
|
963
|
-
// node, on name alone, with a `token_hash` overwrite". A read must
|
|
964
|
-
// never widen who may claim an identity.
|
|
965
|
-
//
|
|
966
|
-
// Precisely: the sweep's status update cannot affect this gate at all
|
|
967
|
-
// any more. The sweep does also clamp a FUTURE `last_seen` back to the
|
|
968
|
-
// server clock, which is a write — but it can only move the reclaim
|
|
969
|
-
// moment later-or-equal relative to that bogus timestamp, never make a
|
|
970
|
-
// row claimable now, since the clamped value is `now` and this
|
|
971
|
-
// predicate needs `now - AGENT_RECLAIM_GRACE_MS`. That clamp exists so
|
|
972
|
-
// a client with a skewed clock cannot make its name permanently
|
|
973
|
-
// unreclaimable; it is deliberate, and it is the only path by which a
|
|
974
|
-
// read touches this column.
|
|
975
|
-
//
|
|
976
|
-
// The grace window is far longer than the presence TTL: an agent goes
|
|
977
|
-
// 'offline' on the roster after 5 minutes of silence, but its name is
|
|
978
|
-
// not reclaimable by a stranger until AGENT_RECLAIM_GRACE_MS. Between
|
|
979
|
-
// those two points the agent reads as away and its identity is still
|
|
980
|
-
// its own.
|
|
981
|
-
//
|
|
982
|
-
// The second disjunct is unchanged: the agent's own node may always
|
|
983
|
-
// re-register it, so a node restart or reconnect is never blocked by
|
|
984
|
-
// the grace window.
|
|
985
|
-
setWhere: or(lt(agents.lastSeen, new Date(Date.now() - AGENT_RECLAIM_GRACE_MS)), and(eq(agents.locationType, 'via_node'), or(eq(agents.locationNodeId, nodeId), sql `${agents.locationNodeId} = 'node_direct_' || ${agents.id}`))),
|
|
986
|
-
})
|
|
937
|
+
.onConflictDoNothing({ target: [agents.workspaceId, agents.name] })
|
|
987
938
|
.returning();
|
|
988
939
|
if (!result) {
|
|
989
|
-
throw codedError(`Agent "${message.name}" already
|
|
940
|
+
throw codedError(`Agent "${message.name}" already exists; use agent.recover with proof of the immutable id`, 'agent_already_exists', 409);
|
|
990
941
|
}
|
|
991
942
|
await autoJoinGeneral(tx, workspaceId, result.id);
|
|
992
943
|
const activeNodeIds = await activeBindingNodeIdsForAgent(tx, workspaceId, result.id);
|
|
@@ -1017,6 +968,108 @@ export async function registerAgentViaNode(db, workspaceId, nodeId, providerName
|
|
|
1017
968
|
};
|
|
1018
969
|
});
|
|
1019
970
|
}
|
|
971
|
+
/**
|
|
972
|
+
* Recover an existing agent using the authenticated node as the authority.
|
|
973
|
+
* The immutable id and server-owned `origin_node_id` must both match; name,
|
|
974
|
+
* presence, silence, and the synthetic direct-node convention grant nothing.
|
|
975
|
+
*/
|
|
976
|
+
export async function recoverAgentViaNode(db, workspaceId, nodeId, providerName, message, options = {}) {
|
|
977
|
+
assertRegistrableAgentName(message.name);
|
|
978
|
+
const hasInteractiveTransaction = typeof db.withTransaction === 'function';
|
|
979
|
+
return runAtomic(db, async (tx) => {
|
|
980
|
+
const [node] = await tx
|
|
981
|
+
.select()
|
|
982
|
+
.from(nodes)
|
|
983
|
+
.where(and(eq(nodes.workspaceId, workspaceId), eq(nodes.id, nodeId)));
|
|
984
|
+
if (!node)
|
|
985
|
+
throw codedError(`Node "${nodeId}" not found`, 'node_not_found', 404);
|
|
986
|
+
const [provider] = await tx
|
|
987
|
+
.select({ capabilities: nodeProviders.capabilities })
|
|
988
|
+
.from(nodeProviders)
|
|
989
|
+
.where(and(eq(nodeProviders.workspaceId, workspaceId), eq(nodeProviders.nodeId, nodeId), eq(nodeProviders.name, providerName)));
|
|
990
|
+
const cursorHandshake = (options.deliveryCursorSupported ?? true) && (provider?.capabilities?.some((capability) => capability.name === FLEET_DELIVERY_CURSOR_CAPABILITY) ?? false);
|
|
991
|
+
const [target] = await tx
|
|
992
|
+
.select()
|
|
993
|
+
.from(agents)
|
|
994
|
+
.where(and(eq(agents.workspaceId, workspaceId), eq(agents.name, message.name)));
|
|
995
|
+
if (!target) {
|
|
996
|
+
throw codedError(`Agent "${message.name}" not found`, 'agent_not_found', 404);
|
|
997
|
+
}
|
|
998
|
+
if (target.id !== message.expected_agent_id || target.originNodeId !== nodeId) {
|
|
999
|
+
throw codedError(`Node "${nodeId}" does not own expected agent "${message.expected_agent_id}"`, 'agent_recovery_not_authorized', 403);
|
|
1000
|
+
}
|
|
1001
|
+
const activeNodeIds = await activeBindingNodeIdsForAgent(tx, workspaceId, target.id);
|
|
1002
|
+
const targetWasActive = activeNodeIds.includes(nodeId);
|
|
1003
|
+
let reservedTargetSlot = false;
|
|
1004
|
+
// D1 has no interactive transaction, so capacity must be acquired before
|
|
1005
|
+
// any agent/location mutation. A full node now fails with the incumbent
|
|
1006
|
+
// row and bindings untouched; a later failure releases this reservation.
|
|
1007
|
+
if (!targetWasActive) {
|
|
1008
|
+
await reserveNodeAgentSlot(tx, workspaceId, node);
|
|
1009
|
+
reservedTargetSlot = true;
|
|
1010
|
+
}
|
|
1011
|
+
try {
|
|
1012
|
+
const now = new Date().toISOString();
|
|
1013
|
+
const fleetMetadata = sql `json_object(
|
|
1014
|
+
'node_id', ${nodeId},
|
|
1015
|
+
'invocation_id', ${message.invocation_id ?? null},
|
|
1016
|
+
'registered_at', ${now}
|
|
1017
|
+
)`;
|
|
1018
|
+
const [result] = await tx
|
|
1019
|
+
.update(agents)
|
|
1020
|
+
.set({
|
|
1021
|
+
status: 'active',
|
|
1022
|
+
lastSeen: new Date(),
|
|
1023
|
+
metadata: sql `json_patch(COALESCE(${agents.metadata}, '{}'), ${fleetMetadata})`,
|
|
1024
|
+
locationType: 'via_node',
|
|
1025
|
+
locationNodeId: nodeId,
|
|
1026
|
+
providerName,
|
|
1027
|
+
resumable: message.resumable ?? false,
|
|
1028
|
+
sessionRef: message.session_ref ?? null,
|
|
1029
|
+
})
|
|
1030
|
+
.where(and(eq(agents.workspaceId, workspaceId), eq(agents.id, message.expected_agent_id), eq(agents.name, message.name), eq(agents.originNodeId, nodeId)))
|
|
1031
|
+
.returning();
|
|
1032
|
+
if (!result) {
|
|
1033
|
+
throw codedError(`Agent "${message.name}" changed during node recovery`, 'agent_identity_conflict', 409);
|
|
1034
|
+
}
|
|
1035
|
+
await autoJoinGeneral(tx, workspaceId, result.id);
|
|
1036
|
+
await upsertAgentNodeBinding(tx, workspaceId, result, nodeId, {
|
|
1037
|
+
sessionRef: message.session_ref ?? null,
|
|
1038
|
+
deactivateExisting: true,
|
|
1039
|
+
});
|
|
1040
|
+
await releaseNodeAgentSlots(tx, workspaceId, activeNodeIds.filter((activeNodeId) => activeNodeId !== nodeId));
|
|
1041
|
+
// Rotate only after every fallible routing/capacity mutation. On Node the
|
|
1042
|
+
// surrounding transaction still makes the whole recovery atomic; on D1
|
|
1043
|
+
// the rotation+audit pair remains one atomic batch.
|
|
1044
|
+
const rotated = await rotateAgentIdentity(tx, {
|
|
1045
|
+
workspaceId,
|
|
1046
|
+
agentId: target.id,
|
|
1047
|
+
agentName: target.name,
|
|
1048
|
+
}, {
|
|
1049
|
+
authority: 'origin_node',
|
|
1050
|
+
actor: `node:${node.name}`,
|
|
1051
|
+
reason: 'explicit node identity recovery',
|
|
1052
|
+
sessionRef: message.session_ref ?? target.sessionRef,
|
|
1053
|
+
nodeId,
|
|
1054
|
+
originActor: 'node-control/agent.recover',
|
|
1055
|
+
}, 'recover', {
|
|
1056
|
+
requireAtomic: !hasInteractiveTransaction,
|
|
1057
|
+
alreadyAtomic: hasInteractiveTransaction,
|
|
1058
|
+
});
|
|
1059
|
+
return {
|
|
1060
|
+
agent_id: result.id,
|
|
1061
|
+
name: result.name,
|
|
1062
|
+
token: rotated.token,
|
|
1063
|
+
...(cursorHandshake ? { delivery_ack_seq: result.deliveryAckSeq } : {}),
|
|
1064
|
+
};
|
|
1065
|
+
}
|
|
1066
|
+
catch (err) {
|
|
1067
|
+
if (reservedTargetSlot)
|
|
1068
|
+
await releaseNodeAgentSlots(tx, workspaceId, [nodeId]);
|
|
1069
|
+
throw err;
|
|
1070
|
+
}
|
|
1071
|
+
});
|
|
1072
|
+
}
|
|
1020
1073
|
export async function deregisterAgentViaNode(db, workspaceId, nodeId, message, deps) {
|
|
1021
1074
|
const conditions = [
|
|
1022
1075
|
eq(agents.workspaceId, workspaceId),
|
|
@@ -1445,6 +1498,21 @@ export async function handleNodeControlMessage(args) {
|
|
|
1445
1498
|
await deliverPendingToNode(args.db, args.registry, args.workspaceId, args.nodeId, { providerName: frameProviderName, agentIds: [registered.agent_id] });
|
|
1446
1499
|
return;
|
|
1447
1500
|
}
|
|
1501
|
+
case 'agent.recover': {
|
|
1502
|
+
const recovered = await recoverAgentViaNode(args.db, args.workspaceId, args.nodeId, frameProviderName, message, { deliveryCursorSupported: supportsProviderDeliveryReadiness(args.registry) });
|
|
1503
|
+
const replySent = sendControl(args.socket, {
|
|
1504
|
+
v: 1,
|
|
1505
|
+
id: requestId(message),
|
|
1506
|
+
type: 'reply',
|
|
1507
|
+
ok: true,
|
|
1508
|
+
data: recovered,
|
|
1509
|
+
});
|
|
1510
|
+
if (!replySent)
|
|
1511
|
+
return;
|
|
1512
|
+
args.registry.markProviderAgentsDeliveryReady?.(args.workspaceId, args.nodeId, frameProviderName, args.connectionId, [recovered.agent_id]);
|
|
1513
|
+
await deliverPendingToNode(args.db, args.registry, args.workspaceId, args.nodeId, { providerName: frameProviderName, agentIds: [recovered.agent_id] });
|
|
1514
|
+
return;
|
|
1515
|
+
}
|
|
1448
1516
|
case 'agent.deregister':
|
|
1449
1517
|
await deregisterAgentViaNode(args.db, args.workspaceId, args.nodeId, message, args.completionDeps);
|
|
1450
1518
|
return;
|