@geometra/core 1.59.1 → 1.61.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/agent-contracts.d.ts +26 -0
- package/dist/agent-contracts.d.ts.map +1 -0
- package/dist/agent-contracts.js +78 -0
- package/dist/agent-contracts.js.map +1 -0
- package/dist/agent-gateway.d.ts +158 -0
- package/dist/agent-gateway.d.ts.map +1 -0
- package/dist/agent-gateway.js +380 -0
- package/dist/agent-gateway.js.map +1 -0
- package/dist/agent-runtime.d.ts +51 -0
- package/dist/agent-runtime.d.ts.map +1 -0
- package/dist/agent-runtime.js +199 -0
- package/dist/agent-runtime.js.map +1 -0
- package/dist/agent-trace.d.ts +33 -0
- package/dist/agent-trace.d.ts.map +1 -0
- package/dist/agent-trace.js +47 -0
- package/dist/agent-trace.js.map +1 -0
- package/dist/index.d.ts +11 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -0
- package/dist/index.js.map +1 -1
- package/dist/semantic-geometry.d.ts +51 -0
- package/dist/semantic-geometry.d.ts.map +1 -0
- package/dist/semantic-geometry.js +173 -0
- package/dist/semantic-geometry.js.map +1 -0
- package/dist/types.d.ts +41 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { ComputedLayout } from 'textura';
|
|
2
|
+
import type { AgentActionContract, SemanticProps, UIElement } from './types.js';
|
|
3
|
+
export interface AgentActionTarget {
|
|
4
|
+
id: string;
|
|
5
|
+
kind: AgentActionContract['kind'];
|
|
6
|
+
title: string;
|
|
7
|
+
description?: string;
|
|
8
|
+
risk: NonNullable<AgentActionContract['risk']>;
|
|
9
|
+
requiresConfirmation: boolean;
|
|
10
|
+
enabled: boolean;
|
|
11
|
+
role: string;
|
|
12
|
+
name?: string;
|
|
13
|
+
bounds: {
|
|
14
|
+
x: number;
|
|
15
|
+
y: number;
|
|
16
|
+
width: number;
|
|
17
|
+
height: number;
|
|
18
|
+
};
|
|
19
|
+
path: number[];
|
|
20
|
+
contract: AgentActionContract;
|
|
21
|
+
}
|
|
22
|
+
/** Attach an agent action contract to existing semantic metadata. */
|
|
23
|
+
export declare function agentAction(contract: AgentActionContract, semantic?: Omit<SemanticProps, 'agentAction'>): SemanticProps;
|
|
24
|
+
/** Extract all agent-native action contracts from a rendered tree and its computed layout. */
|
|
25
|
+
export declare function collectAgentActions(tree: UIElement, layout: ComputedLayout): AgentActionTarget[];
|
|
26
|
+
//# sourceMappingURL=agent-contracts.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-contracts.d.ts","sourceRoot":"","sources":["../src/agent-contracts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAA;AAC7C,OAAO,KAAK,EAAE,mBAAmB,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAG/E,MAAM,WAAW,iBAAiB;IAChC,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,mBAAmB,CAAC,MAAM,CAAC,CAAA;IACjC,KAAK,EAAE,MAAM,CAAA;IACb,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,IAAI,EAAE,WAAW,CAAC,mBAAmB,CAAC,MAAM,CAAC,CAAC,CAAA;IAC9C,oBAAoB,EAAE,OAAO,CAAA;IAC7B,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAA;IAC/D,IAAI,EAAE,MAAM,EAAE,CAAA;IACd,QAAQ,EAAE,mBAAmB,CAAA;CAC9B;AA0BD,qEAAqE;AACrE,wBAAgB,WAAW,CACzB,QAAQ,EAAE,mBAAmB,EAC7B,QAAQ,GAAE,IAAI,CAAC,aAAa,EAAE,aAAa,CAAM,GAChD,aAAa,CAEf;AAgDD,8FAA8F;AAC9F,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,EAAE,cAAc,GAAG,iBAAiB,EAAE,CAIhG"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { layoutBoundsAreFinite, scrollSafeChildOffsets } from './layout-bounds.js';
|
|
2
|
+
function roleFor(element) {
|
|
3
|
+
if (element.semantic?.role)
|
|
4
|
+
return element.semantic.role;
|
|
5
|
+
if (element.kind === 'text')
|
|
6
|
+
return 'text';
|
|
7
|
+
if (element.kind === 'image' || element.kind === 'scene3d')
|
|
8
|
+
return 'img';
|
|
9
|
+
if (element.kind === 'box' && element.handlers?.onClick)
|
|
10
|
+
return 'button';
|
|
11
|
+
return 'group';
|
|
12
|
+
}
|
|
13
|
+
function nameFor(element) {
|
|
14
|
+
if (element.semantic?.ariaLabel)
|
|
15
|
+
return element.semantic.ariaLabel;
|
|
16
|
+
if (element.kind === 'text')
|
|
17
|
+
return element.props.text;
|
|
18
|
+
if (element.kind === 'image')
|
|
19
|
+
return element.semantic?.alt ?? element.props.alt;
|
|
20
|
+
return element.semantic?.alt;
|
|
21
|
+
}
|
|
22
|
+
function normalizeRisk(contract) {
|
|
23
|
+
return contract.risk ?? 'write';
|
|
24
|
+
}
|
|
25
|
+
function normalizeConfirmation(contract) {
|
|
26
|
+
if (contract.requiresConfirmation !== undefined)
|
|
27
|
+
return contract.requiresConfirmation;
|
|
28
|
+
return normalizeRisk(contract) === 'destructive' || normalizeRisk(contract) === 'external';
|
|
29
|
+
}
|
|
30
|
+
/** Attach an agent action contract to existing semantic metadata. */
|
|
31
|
+
export function agentAction(contract, semantic = {}) {
|
|
32
|
+
return { ...semantic, agentAction: contract };
|
|
33
|
+
}
|
|
34
|
+
function walkAgentActions(element, layout, offsetX, offsetY, path, out) {
|
|
35
|
+
if (!layoutBoundsAreFinite(layout))
|
|
36
|
+
return;
|
|
37
|
+
const x = offsetX + layout.x;
|
|
38
|
+
const y = offsetY + layout.y;
|
|
39
|
+
if (!Number.isFinite(x) || !Number.isFinite(y))
|
|
40
|
+
return;
|
|
41
|
+
const contract = element.semantic?.agentAction;
|
|
42
|
+
if (contract) {
|
|
43
|
+
const name = nameFor(element);
|
|
44
|
+
out.push({
|
|
45
|
+
id: contract.id,
|
|
46
|
+
kind: contract.kind,
|
|
47
|
+
title: contract.title,
|
|
48
|
+
...(contract.description !== undefined ? { description: contract.description } : {}),
|
|
49
|
+
risk: normalizeRisk(contract),
|
|
50
|
+
requiresConfirmation: normalizeConfirmation(contract),
|
|
51
|
+
enabled: element.semantic?.ariaDisabled !== true,
|
|
52
|
+
role: roleFor(element),
|
|
53
|
+
...(name !== undefined ? { name } : {}),
|
|
54
|
+
bounds: { x, y, width: layout.width, height: layout.height },
|
|
55
|
+
path,
|
|
56
|
+
contract,
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
if (element.kind !== 'box')
|
|
60
|
+
return;
|
|
61
|
+
const childOrigin = scrollSafeChildOffsets(x, y, element.props.scrollX, element.props.scrollY);
|
|
62
|
+
if (!childOrigin)
|
|
63
|
+
return;
|
|
64
|
+
for (let i = 0; i < element.children.length; i++) {
|
|
65
|
+
const child = element.children[i];
|
|
66
|
+
const childLayout = layout.children[i];
|
|
67
|
+
if (child && childLayout) {
|
|
68
|
+
walkAgentActions(child, childLayout, childOrigin.ox, childOrigin.oy, [...path, i], out);
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
/** Extract all agent-native action contracts from a rendered tree and its computed layout. */
|
|
73
|
+
export function collectAgentActions(tree, layout) {
|
|
74
|
+
const out = [];
|
|
75
|
+
walkAgentActions(tree, layout, 0, 0, [], out);
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=agent-contracts.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-contracts.js","sourceRoot":"","sources":["../src/agent-contracts.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import type { ComputedLayout } from 'textura';
|
|
2
|
+
import type { AgentTrace } from './agent-trace.js';
|
|
3
|
+
import type { AgentActionRisk, UIElement } from './types.js';
|
|
4
|
+
import { type AgentActionTarget } from './agent-contracts.js';
|
|
5
|
+
import { type AgentGeometrySnapshot } from './semantic-geometry.js';
|
|
6
|
+
export type AgentGatewayActionStatus = 'completed' | 'awaiting_approval' | 'denied' | 'disabled' | 'failed' | 'no_frame' | 'not_found' | 'stale_frame' | 'approval_not_found';
|
|
7
|
+
export interface AgentGatewayFrame {
|
|
8
|
+
id: string;
|
|
9
|
+
route?: string;
|
|
10
|
+
createdAt: string;
|
|
11
|
+
tree: UIElement;
|
|
12
|
+
layout: ComputedLayout;
|
|
13
|
+
geometry: AgentGeometrySnapshot;
|
|
14
|
+
actions: AgentActionTarget[];
|
|
15
|
+
trace: AgentTrace;
|
|
16
|
+
}
|
|
17
|
+
export interface AgentGatewayFrameSnapshot {
|
|
18
|
+
id: string;
|
|
19
|
+
route?: string;
|
|
20
|
+
createdAt: string;
|
|
21
|
+
layout: ComputedLayout;
|
|
22
|
+
geometry: AgentGeometrySnapshot;
|
|
23
|
+
actions: AgentActionTarget[];
|
|
24
|
+
}
|
|
25
|
+
export interface AgentGatewayFrameOptions {
|
|
26
|
+
id?: string;
|
|
27
|
+
route?: string;
|
|
28
|
+
createdAt?: string;
|
|
29
|
+
}
|
|
30
|
+
export interface AgentGatewayActionRequest {
|
|
31
|
+
actionId: string;
|
|
32
|
+
frameId?: string;
|
|
33
|
+
actor?: string;
|
|
34
|
+
approvalActor?: string;
|
|
35
|
+
approved?: boolean;
|
|
36
|
+
input?: unknown;
|
|
37
|
+
}
|
|
38
|
+
export interface AgentGatewayApprovalRequest {
|
|
39
|
+
approvalId: string;
|
|
40
|
+
actor?: string;
|
|
41
|
+
approved?: boolean;
|
|
42
|
+
}
|
|
43
|
+
export type AgentGatewayPolicyDecision = {
|
|
44
|
+
allow: true;
|
|
45
|
+
/** Require approval in addition to any requirement declared by the contract. */
|
|
46
|
+
requiresApproval?: boolean;
|
|
47
|
+
reason?: string;
|
|
48
|
+
} | {
|
|
49
|
+
allow: false;
|
|
50
|
+
reason: string;
|
|
51
|
+
};
|
|
52
|
+
export interface AgentGatewayPolicyContext {
|
|
53
|
+
frame: AgentGatewayFrame;
|
|
54
|
+
target: AgentActionTarget;
|
|
55
|
+
request: AgentGatewayActionRequest;
|
|
56
|
+
}
|
|
57
|
+
export type AgentGatewayPolicy = (context: AgentGatewayPolicyContext) => AgentGatewayPolicyDecision;
|
|
58
|
+
export interface AgentGatewayExecuteContext extends AgentGatewayPolicyContext {
|
|
59
|
+
approved: boolean;
|
|
60
|
+
}
|
|
61
|
+
export type AgentGatewayExecutor = (context: AgentGatewayExecuteContext) => unknown | Promise<unknown>;
|
|
62
|
+
export interface AgentGatewayPendingApproval {
|
|
63
|
+
id: string;
|
|
64
|
+
actionId: string;
|
|
65
|
+
frameId: string;
|
|
66
|
+
requestedAt: string;
|
|
67
|
+
target: AgentActionTarget;
|
|
68
|
+
request: AgentGatewayActionRequest;
|
|
69
|
+
reason: string;
|
|
70
|
+
}
|
|
71
|
+
export type AgentGatewayApprovalHook = (approval: AgentGatewayPendingApproval) => unknown | Promise<unknown>;
|
|
72
|
+
export type AgentGatewayResultHook = (result: AgentGatewayActionResult) => unknown | Promise<unknown>;
|
|
73
|
+
export interface AgentGatewayActionResult {
|
|
74
|
+
status: AgentGatewayActionStatus;
|
|
75
|
+
actionId: string;
|
|
76
|
+
frameId?: string;
|
|
77
|
+
approvalId?: string;
|
|
78
|
+
replayId?: string;
|
|
79
|
+
target?: AgentActionTarget;
|
|
80
|
+
reason?: string;
|
|
81
|
+
output?: unknown;
|
|
82
|
+
trace: AgentTrace;
|
|
83
|
+
}
|
|
84
|
+
export interface AgentGatewayReplayAction {
|
|
85
|
+
id: string;
|
|
86
|
+
actionId: string;
|
|
87
|
+
status: AgentGatewayActionStatus;
|
|
88
|
+
requestedAt: string;
|
|
89
|
+
completedAt?: string;
|
|
90
|
+
frameBefore?: AgentGatewayFrameSnapshot;
|
|
91
|
+
frameAfter?: AgentGatewayFrameSnapshot;
|
|
92
|
+
request: {
|
|
93
|
+
actor: string;
|
|
94
|
+
input?: unknown;
|
|
95
|
+
};
|
|
96
|
+
policy?: AgentGatewayPolicyDecision;
|
|
97
|
+
approval?: {
|
|
98
|
+
id?: string;
|
|
99
|
+
actor?: string;
|
|
100
|
+
approved: boolean;
|
|
101
|
+
timestamp: string;
|
|
102
|
+
};
|
|
103
|
+
output?: unknown;
|
|
104
|
+
error?: string;
|
|
105
|
+
}
|
|
106
|
+
export interface AgentGatewayReplay {
|
|
107
|
+
sessionId: string;
|
|
108
|
+
startedAt: string;
|
|
109
|
+
trace: AgentTrace;
|
|
110
|
+
frames: AgentGatewayFrameSnapshot[];
|
|
111
|
+
actions: AgentGatewayReplayAction[];
|
|
112
|
+
}
|
|
113
|
+
export type AgentGatewayRedactionField = 'input' | 'output' | 'error';
|
|
114
|
+
export interface AgentGatewayRedactionContext {
|
|
115
|
+
field: AgentGatewayRedactionField;
|
|
116
|
+
actionId: string;
|
|
117
|
+
}
|
|
118
|
+
export type AgentGatewayRedactor = (value: unknown, context: AgentGatewayRedactionContext) => unknown;
|
|
119
|
+
export interface AgentGatewayPolicyOptions {
|
|
120
|
+
/** If supplied, only these action ids may run. */
|
|
121
|
+
allowedActionIds?: string[];
|
|
122
|
+
deniedActionIds?: string[];
|
|
123
|
+
/** If supplied, only these risk classes may run. */
|
|
124
|
+
allowedRisks?: AgentActionRisk[];
|
|
125
|
+
/** Risk classes to deny. Defaults to external/destructive unless disabled. */
|
|
126
|
+
deniedRisks?: AgentActionRisk[];
|
|
127
|
+
/** Set true to avoid the default external/destructive deny list. */
|
|
128
|
+
allowExternalDestructiveByDefault?: boolean;
|
|
129
|
+
/** Extra risk classes that must be explicitly approved. */
|
|
130
|
+
requireApprovalForRisks?: AgentActionRisk[];
|
|
131
|
+
}
|
|
132
|
+
export interface AgentGatewayOptions {
|
|
133
|
+
sessionId: string;
|
|
134
|
+
startedAt?: string;
|
|
135
|
+
now?: () => string;
|
|
136
|
+
policy?: AgentGatewayPolicy;
|
|
137
|
+
execute?: AgentGatewayExecutor;
|
|
138
|
+
redact?: AgentGatewayRedactor;
|
|
139
|
+
/** Optional side-effect hook for approval notifications or webhook dispatch. Hook failures are ignored. */
|
|
140
|
+
onApprovalRequired?: AgentGatewayApprovalHook;
|
|
141
|
+
/** Optional side-effect hook for audit sinks or workflow notifications. Hook failures are ignored. */
|
|
142
|
+
onActionResult?: AgentGatewayResultHook;
|
|
143
|
+
}
|
|
144
|
+
export interface AgentGateway {
|
|
145
|
+
setFrame(tree: UIElement, layout: ComputedLayout, options?: AgentGatewayFrameOptions): AgentGatewayFrame;
|
|
146
|
+
getFrame(): AgentGatewayFrame | null;
|
|
147
|
+
listActions(): AgentActionTarget[];
|
|
148
|
+
getTrace(): AgentTrace;
|
|
149
|
+
getReplay(): AgentGatewayReplay;
|
|
150
|
+
getPendingApprovals(): AgentGatewayPendingApproval[];
|
|
151
|
+
requestAction(request: AgentGatewayActionRequest): Promise<AgentGatewayActionResult>;
|
|
152
|
+
approveAction(request: AgentGatewayApprovalRequest): Promise<AgentGatewayActionResult>;
|
|
153
|
+
}
|
|
154
|
+
/** Build a simple allowlist/denylist policy for an agent gateway. */
|
|
155
|
+
export declare function createAgentGatewayPolicy(options?: AgentGatewayPolicyOptions): AgentGatewayPolicy;
|
|
156
|
+
/** Create a frame-bound gateway for listing, policy-checking, executing, tracing, and replaying agent actions. */
|
|
157
|
+
export declare function createAgentGateway(options: AgentGatewayOptions): AgentGateway;
|
|
158
|
+
//# sourceMappingURL=agent-gateway.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agent-gateway.d.ts","sourceRoot":"","sources":["../src/agent-gateway.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,380 @@
|
|
|
1
|
+
import { appendAgentTraceEvent, createAgentTrace } from './agent-trace.js';
|
|
2
|
+
import { collectAgentActions } from './agent-contracts.js';
|
|
3
|
+
import { createAgentGeometrySnapshot } from './semantic-geometry.js';
|
|
4
|
+
function defaultNow() {
|
|
5
|
+
return new Date().toISOString();
|
|
6
|
+
}
|
|
7
|
+
function idSet(values) {
|
|
8
|
+
return values ? new Set(values) : null;
|
|
9
|
+
}
|
|
10
|
+
function riskSet(values) {
|
|
11
|
+
return values ? new Set(values) : null;
|
|
12
|
+
}
|
|
13
|
+
function errorMessage(error) {
|
|
14
|
+
return error instanceof Error ? error.message : String(error);
|
|
15
|
+
}
|
|
16
|
+
function defaultRedact(value) {
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
function frameSnapshot(frame) {
|
|
20
|
+
return {
|
|
21
|
+
id: frame.id,
|
|
22
|
+
...(frame.route !== undefined ? { route: frame.route } : {}),
|
|
23
|
+
createdAt: frame.createdAt,
|
|
24
|
+
layout: frame.layout,
|
|
25
|
+
geometry: frame.geometry,
|
|
26
|
+
actions: frame.actions,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
/** Build a simple allowlist/denylist policy for an agent gateway. */
|
|
30
|
+
export function createAgentGatewayPolicy(options = {}) {
|
|
31
|
+
const allowedActionIds = idSet(options.allowedActionIds);
|
|
32
|
+
const deniedActionIds = idSet(options.deniedActionIds);
|
|
33
|
+
const allowedRisks = riskSet(options.allowedRisks);
|
|
34
|
+
const deniedRisks = riskSet(options.deniedRisks ??
|
|
35
|
+
(options.allowExternalDestructiveByDefault === true ? [] : ['external', 'destructive']));
|
|
36
|
+
const requireApprovalForRisks = riskSet(options.requireApprovalForRisks);
|
|
37
|
+
return ({ target }) => {
|
|
38
|
+
if (deniedActionIds?.has(target.id)) {
|
|
39
|
+
return { allow: false, reason: `action "${target.id}" is denied by policy` };
|
|
40
|
+
}
|
|
41
|
+
if (allowedActionIds && !allowedActionIds.has(target.id)) {
|
|
42
|
+
return { allow: false, reason: `action "${target.id}" is not allowlisted` };
|
|
43
|
+
}
|
|
44
|
+
if (deniedRisks?.has(target.risk)) {
|
|
45
|
+
return { allow: false, reason: `risk "${target.risk}" is denied by policy` };
|
|
46
|
+
}
|
|
47
|
+
if (allowedRisks && !allowedRisks.has(target.risk)) {
|
|
48
|
+
return { allow: false, reason: `risk "${target.risk}" is not allowed` };
|
|
49
|
+
}
|
|
50
|
+
return {
|
|
51
|
+
allow: true,
|
|
52
|
+
...(requireApprovalForRisks?.has(target.risk) ? { requiresApproval: true } : {}),
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
/** Create a frame-bound gateway for listing, policy-checking, executing, tracing, and replaying agent actions. */
|
|
57
|
+
export function createAgentGateway(options) {
|
|
58
|
+
const now = options.now ?? defaultNow;
|
|
59
|
+
const redact = options.redact ?? defaultRedact;
|
|
60
|
+
let trace = createAgentTrace(options.sessionId, options.startedAt ?? now());
|
|
61
|
+
let frame = null;
|
|
62
|
+
let frameIndex = 0;
|
|
63
|
+
let approvalIndex = 0;
|
|
64
|
+
let replayIndex = 0;
|
|
65
|
+
const frames = [];
|
|
66
|
+
const replayActions = [];
|
|
67
|
+
const pendingApprovals = new Map();
|
|
68
|
+
const replayActionsNeedingFrameAfter = new Set();
|
|
69
|
+
const redactValue = (actionId, field, value) => redact(value, { actionId, field });
|
|
70
|
+
const runHook = (hook, value) => {
|
|
71
|
+
try {
|
|
72
|
+
void Promise.resolve(hook?.(value)).catch(() => undefined);
|
|
73
|
+
}
|
|
74
|
+
catch {
|
|
75
|
+
// Hooks are notification sinks; they must not change gateway execution.
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
const resultWithHook = (result) => {
|
|
79
|
+
runHook(options.onActionResult, result);
|
|
80
|
+
return result;
|
|
81
|
+
};
|
|
82
|
+
const withTrace = () => {
|
|
83
|
+
if (frame)
|
|
84
|
+
frame = { ...frame, trace };
|
|
85
|
+
};
|
|
86
|
+
const append = (actionId, status, event = {}) => {
|
|
87
|
+
trace = appendAgentTraceEvent(trace, {
|
|
88
|
+
actionId,
|
|
89
|
+
status,
|
|
90
|
+
timestamp: now(),
|
|
91
|
+
...(event.actor !== undefined ? { actor: event.actor } : {}),
|
|
92
|
+
...(event.input !== undefined ? { input: redactValue(actionId, 'input', event.input) } : {}),
|
|
93
|
+
...(event.output !== undefined ? { output: redactValue(actionId, 'output', event.output) } : {}),
|
|
94
|
+
...(event.error !== undefined ? { error: String(redactValue(actionId, 'error', event.error)) } : {}),
|
|
95
|
+
...(event.message !== undefined ? { message: event.message } : {}),
|
|
96
|
+
});
|
|
97
|
+
withTrace();
|
|
98
|
+
};
|
|
99
|
+
const createReplayAction = (request, status, frameBefore) => {
|
|
100
|
+
replayIndex++;
|
|
101
|
+
const action = {
|
|
102
|
+
id: `${options.sessionId}:replay:${replayIndex}`,
|
|
103
|
+
actionId: request.actionId,
|
|
104
|
+
status,
|
|
105
|
+
requestedAt: now(),
|
|
106
|
+
...(frameBefore ? { frameBefore } : {}),
|
|
107
|
+
request: {
|
|
108
|
+
actor: request.actor ?? 'agent',
|
|
109
|
+
...(request.input !== undefined ? { input: redactValue(request.actionId, 'input', request.input) } : {}),
|
|
110
|
+
},
|
|
111
|
+
};
|
|
112
|
+
replayActions.push(action);
|
|
113
|
+
return action;
|
|
114
|
+
};
|
|
115
|
+
const finishReplayAction = (replayId, patch) => {
|
|
116
|
+
const index = replayActions.findIndex(action => action.id === replayId);
|
|
117
|
+
if (index === -1)
|
|
118
|
+
return;
|
|
119
|
+
replayActions[index] = {
|
|
120
|
+
...replayActions[index],
|
|
121
|
+
...patch,
|
|
122
|
+
completedAt: patch.completedAt ?? now(),
|
|
123
|
+
};
|
|
124
|
+
replayActionsNeedingFrameAfter.add(replayId);
|
|
125
|
+
};
|
|
126
|
+
const reject = (request, status, reason, target) => {
|
|
127
|
+
const replay = createReplayAction(request, status, frame ? frameSnapshot(frame) : undefined);
|
|
128
|
+
append(request.actionId, 'requested', {
|
|
129
|
+
actor: request.actor ?? 'agent',
|
|
130
|
+
input: request.input,
|
|
131
|
+
message: reason,
|
|
132
|
+
});
|
|
133
|
+
append(request.actionId, 'denied', {
|
|
134
|
+
actor: 'gateway',
|
|
135
|
+
error: reason,
|
|
136
|
+
});
|
|
137
|
+
finishReplayAction(replay.id, {
|
|
138
|
+
status,
|
|
139
|
+
error: String(redactValue(request.actionId, 'error', reason)),
|
|
140
|
+
});
|
|
141
|
+
return resultWithHook({
|
|
142
|
+
status,
|
|
143
|
+
actionId: request.actionId,
|
|
144
|
+
replayId: replay.id,
|
|
145
|
+
...(frame ? { frameId: frame.id } : {}),
|
|
146
|
+
...(target ? { target } : {}),
|
|
147
|
+
reason,
|
|
148
|
+
trace,
|
|
149
|
+
});
|
|
150
|
+
};
|
|
151
|
+
const executeApproved = async (sourceFrame, target, request, replayId, approved, approvalActor) => {
|
|
152
|
+
const approvalTimestamp = now();
|
|
153
|
+
if (approved) {
|
|
154
|
+
append(request.actionId, 'approved', {
|
|
155
|
+
actor: approvalActor ?? request.approvalActor ?? 'human',
|
|
156
|
+
message: 'approval recorded',
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
else {
|
|
160
|
+
append(request.actionId, 'denied', {
|
|
161
|
+
actor: approvalActor ?? request.approvalActor ?? 'human',
|
|
162
|
+
error: 'approval denied',
|
|
163
|
+
});
|
|
164
|
+
finishReplayAction(replayId, {
|
|
165
|
+
status: 'denied',
|
|
166
|
+
approval: { actor: approvalActor ?? request.approvalActor ?? 'human', approved: false, timestamp: approvalTimestamp },
|
|
167
|
+
error: 'approval denied',
|
|
168
|
+
});
|
|
169
|
+
return resultWithHook({
|
|
170
|
+
status: 'denied',
|
|
171
|
+
actionId: request.actionId,
|
|
172
|
+
frameId: sourceFrame.id,
|
|
173
|
+
replayId,
|
|
174
|
+
target,
|
|
175
|
+
reason: 'approval denied',
|
|
176
|
+
trace,
|
|
177
|
+
});
|
|
178
|
+
}
|
|
179
|
+
try {
|
|
180
|
+
const output = await options.execute?.({ frame: sourceFrame, target, request, approved: true });
|
|
181
|
+
const redactedOutput = output === undefined ? { accepted: true } : redactValue(request.actionId, 'output', output);
|
|
182
|
+
append(request.actionId, 'completed', {
|
|
183
|
+
actor: 'gateway',
|
|
184
|
+
output: redactedOutput,
|
|
185
|
+
});
|
|
186
|
+
finishReplayAction(replayId, {
|
|
187
|
+
status: 'completed',
|
|
188
|
+
approval: { actor: approvalActor ?? request.approvalActor ?? 'human', approved: true, timestamp: approvalTimestamp },
|
|
189
|
+
output: redactedOutput,
|
|
190
|
+
});
|
|
191
|
+
return resultWithHook({
|
|
192
|
+
status: 'completed',
|
|
193
|
+
actionId: request.actionId,
|
|
194
|
+
frameId: sourceFrame.id,
|
|
195
|
+
replayId,
|
|
196
|
+
target,
|
|
197
|
+
output: redactedOutput,
|
|
198
|
+
trace,
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
catch (error) {
|
|
202
|
+
const message = errorMessage(error);
|
|
203
|
+
append(request.actionId, 'failed', {
|
|
204
|
+
actor: 'gateway',
|
|
205
|
+
error: message,
|
|
206
|
+
});
|
|
207
|
+
finishReplayAction(replayId, {
|
|
208
|
+
status: 'failed',
|
|
209
|
+
approval: { actor: approvalActor ?? request.approvalActor ?? 'human', approved: true, timestamp: approvalTimestamp },
|
|
210
|
+
error: String(redactValue(request.actionId, 'error', message)),
|
|
211
|
+
});
|
|
212
|
+
return resultWithHook({
|
|
213
|
+
status: 'failed',
|
|
214
|
+
actionId: request.actionId,
|
|
215
|
+
frameId: sourceFrame.id,
|
|
216
|
+
replayId,
|
|
217
|
+
target,
|
|
218
|
+
reason: message,
|
|
219
|
+
trace,
|
|
220
|
+
});
|
|
221
|
+
}
|
|
222
|
+
};
|
|
223
|
+
return {
|
|
224
|
+
setFrame(tree, layout, frameOptions = {}) {
|
|
225
|
+
frameIndex++;
|
|
226
|
+
const createdAt = frameOptions.createdAt ?? now();
|
|
227
|
+
const frameId = frameOptions.id ?? `${options.sessionId}:frame:${frameIndex}`;
|
|
228
|
+
const actions = collectAgentActions(tree, layout);
|
|
229
|
+
frame = {
|
|
230
|
+
id: frameId,
|
|
231
|
+
...(frameOptions.route !== undefined ? { route: frameOptions.route } : {}),
|
|
232
|
+
createdAt,
|
|
233
|
+
tree,
|
|
234
|
+
layout,
|
|
235
|
+
geometry: createAgentGeometrySnapshot(tree, layout, {
|
|
236
|
+
id: frameId,
|
|
237
|
+
...(frameOptions.route !== undefined ? { route: frameOptions.route } : {}),
|
|
238
|
+
createdAt,
|
|
239
|
+
}),
|
|
240
|
+
actions,
|
|
241
|
+
trace,
|
|
242
|
+
};
|
|
243
|
+
const snapshot = frameSnapshot(frame);
|
|
244
|
+
frames.push(snapshot);
|
|
245
|
+
for (const replayId of replayActionsNeedingFrameAfter) {
|
|
246
|
+
const action = replayActions.find(item => item.id === replayId);
|
|
247
|
+
if (action && !action.frameAfter) {
|
|
248
|
+
action.frameAfter = snapshot;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
replayActionsNeedingFrameAfter.clear();
|
|
252
|
+
return frame;
|
|
253
|
+
},
|
|
254
|
+
getFrame() {
|
|
255
|
+
return frame;
|
|
256
|
+
},
|
|
257
|
+
listActions() {
|
|
258
|
+
return frame?.actions ?? [];
|
|
259
|
+
},
|
|
260
|
+
getTrace() {
|
|
261
|
+
return trace;
|
|
262
|
+
},
|
|
263
|
+
getReplay() {
|
|
264
|
+
return {
|
|
265
|
+
sessionId: options.sessionId,
|
|
266
|
+
startedAt: trace.startedAt,
|
|
267
|
+
trace,
|
|
268
|
+
frames,
|
|
269
|
+
actions: replayActions,
|
|
270
|
+
};
|
|
271
|
+
},
|
|
272
|
+
getPendingApprovals() {
|
|
273
|
+
return [...pendingApprovals.values()].map(record => ({
|
|
274
|
+
id: record.id,
|
|
275
|
+
actionId: record.actionId,
|
|
276
|
+
frameId: record.frameId,
|
|
277
|
+
requestedAt: record.requestedAt,
|
|
278
|
+
target: record.target,
|
|
279
|
+
request: record.request,
|
|
280
|
+
reason: record.reason,
|
|
281
|
+
}));
|
|
282
|
+
},
|
|
283
|
+
async requestAction(request) {
|
|
284
|
+
if (!frame) {
|
|
285
|
+
return reject(request, 'no_frame', 'no current frame is available');
|
|
286
|
+
}
|
|
287
|
+
if (request.frameId && request.frameId !== frame.id) {
|
|
288
|
+
return reject(request, 'stale_frame', `request frame "${request.frameId}" does not match current frame "${frame.id}"`);
|
|
289
|
+
}
|
|
290
|
+
const sourceFrame = frame;
|
|
291
|
+
const target = sourceFrame.actions.find(action => action.id === request.actionId);
|
|
292
|
+
if (!target) {
|
|
293
|
+
return reject(request, 'not_found', `action "${request.actionId}" is not present in the current frame`);
|
|
294
|
+
}
|
|
295
|
+
if (!target.enabled) {
|
|
296
|
+
return reject(request, 'disabled', `action "${request.actionId}" is disabled`, target);
|
|
297
|
+
}
|
|
298
|
+
const replay = createReplayAction(request, 'awaiting_approval', frameSnapshot(sourceFrame));
|
|
299
|
+
append(request.actionId, 'requested', {
|
|
300
|
+
actor: request.actor ?? 'agent',
|
|
301
|
+
input: request.input,
|
|
302
|
+
message: target.title,
|
|
303
|
+
});
|
|
304
|
+
const policyDecision = options.policy?.({ frame: sourceFrame, target, request }) ?? { allow: true };
|
|
305
|
+
replay.policy = policyDecision;
|
|
306
|
+
if (!policyDecision.allow) {
|
|
307
|
+
append(request.actionId, 'denied', {
|
|
308
|
+
actor: 'gateway',
|
|
309
|
+
error: policyDecision.reason,
|
|
310
|
+
});
|
|
311
|
+
finishReplayAction(replay.id, {
|
|
312
|
+
status: 'denied',
|
|
313
|
+
error: String(redactValue(request.actionId, 'error', policyDecision.reason)),
|
|
314
|
+
});
|
|
315
|
+
return resultWithHook({
|
|
316
|
+
status: 'denied',
|
|
317
|
+
actionId: request.actionId,
|
|
318
|
+
frameId: sourceFrame.id,
|
|
319
|
+
replayId: replay.id,
|
|
320
|
+
target,
|
|
321
|
+
reason: policyDecision.reason,
|
|
322
|
+
trace,
|
|
323
|
+
});
|
|
324
|
+
}
|
|
325
|
+
const approvalRequired = policyDecision.requiresApproval === true || target.requiresConfirmation;
|
|
326
|
+
if (approvalRequired && request.approved !== true) {
|
|
327
|
+
approvalIndex++;
|
|
328
|
+
const approvalId = `${options.sessionId}:approval:${approvalIndex}`;
|
|
329
|
+
const pendingApproval = {
|
|
330
|
+
id: approvalId,
|
|
331
|
+
actionId: request.actionId,
|
|
332
|
+
frameId: sourceFrame.id,
|
|
333
|
+
requestedAt: now(),
|
|
334
|
+
target,
|
|
335
|
+
request,
|
|
336
|
+
reason: policyDecision.reason ?? 'approval required',
|
|
337
|
+
replayId: replay.id,
|
|
338
|
+
policy: policyDecision,
|
|
339
|
+
frame: sourceFrame,
|
|
340
|
+
};
|
|
341
|
+
pendingApprovals.set(approvalId, pendingApproval);
|
|
342
|
+
runHook(options.onApprovalRequired, {
|
|
343
|
+
id: pendingApproval.id,
|
|
344
|
+
actionId: pendingApproval.actionId,
|
|
345
|
+
frameId: pendingApproval.frameId,
|
|
346
|
+
requestedAt: pendingApproval.requestedAt,
|
|
347
|
+
target: pendingApproval.target,
|
|
348
|
+
request: pendingApproval.request,
|
|
349
|
+
reason: pendingApproval.reason,
|
|
350
|
+
});
|
|
351
|
+
return resultWithHook({
|
|
352
|
+
status: 'awaiting_approval',
|
|
353
|
+
actionId: request.actionId,
|
|
354
|
+
frameId: sourceFrame.id,
|
|
355
|
+
approvalId,
|
|
356
|
+
replayId: replay.id,
|
|
357
|
+
target,
|
|
358
|
+
reason: policyDecision.reason ?? 'approval required',
|
|
359
|
+
trace,
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
return executeApproved(sourceFrame, target, request, replay.id, true, request.approvalActor);
|
|
363
|
+
},
|
|
364
|
+
async approveAction(request) {
|
|
365
|
+
const pending = pendingApprovals.get(request.approvalId);
|
|
366
|
+
if (!pending) {
|
|
367
|
+
return resultWithHook({
|
|
368
|
+
status: 'approval_not_found',
|
|
369
|
+
actionId: '',
|
|
370
|
+
approvalId: request.approvalId,
|
|
371
|
+
reason: `approval "${request.approvalId}" is not pending`,
|
|
372
|
+
trace,
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
pendingApprovals.delete(request.approvalId);
|
|
376
|
+
return executeApproved(pending.frame, pending.target, pending.request, pending.replayId, request.approved !== false, request.actor);
|
|
377
|
+
},
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
//# sourceMappingURL=agent-gateway.js.map
|