@axiastudio/aioc 0.1.0-alpha.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/LICENSE +21 -0
- package/README.md +77 -0
- package/dist/agent.d.ts +29 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +36 -0
- package/dist/config.d.ts +5 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +18 -0
- package/dist/errors.d.ts +37 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +46 -0
- package/dist/examples/basic/hello-world.d.ts +2 -0
- package/dist/examples/basic/hello-world.d.ts.map +1 -0
- package/dist/examples/basic/hello-world.js +20 -0
- package/dist/examples/basic/run-record-sink.d.ts +2 -0
- package/dist/examples/basic/run-record-sink.d.ts.map +1 -0
- package/dist/examples/basic/run-record-sink.js +103 -0
- package/dist/examples/basic/tools.d.ts +2 -0
- package/dist/examples/basic/tools.d.ts.map +1 -0
- package/dist/examples/basic/tools.js +84 -0
- package/dist/examples/guardrail-smoke.d.ts +2 -0
- package/dist/examples/guardrail-smoke.d.ts.map +1 -0
- package/dist/examples/guardrail-smoke.js +110 -0
- package/dist/examples/hello-run.d.ts +2 -0
- package/dist/examples/hello-run.d.ts.map +1 -0
- package/dist/examples/hello-run.js +20 -0
- package/dist/examples/learn-01-hello-run.d.ts +2 -0
- package/dist/examples/learn-01-hello-run.d.ts.map +1 -0
- package/dist/examples/learn-01-hello-run.js +29 -0
- package/dist/examples/learn-02-tool-policy.d.ts +2 -0
- package/dist/examples/learn-02-tool-policy.d.ts.map +1 -0
- package/dist/examples/learn-02-tool-policy.js +87 -0
- package/dist/examples/learn-03-controlled-handoff.d.ts +2 -0
- package/dist/examples/learn-03-controlled-handoff.d.ts.map +1 -0
- package/dist/examples/learn-03-controlled-handoff.js +84 -0
- package/dist/examples/learn-04-output-guardrail.d.ts +2 -0
- package/dist/examples/learn-04-output-guardrail.d.ts.map +1 -0
- package/dist/examples/learn-04-output-guardrail.js +61 -0
- package/dist/examples/learn-05-run-record-audit.d.ts +2 -0
- package/dist/examples/learn-05-run-record-audit.d.ts.map +1 -0
- package/dist/examples/learn-05-run-record-audit.js +135 -0
- package/dist/examples/mistral-smoke.d.ts +2 -0
- package/dist/examples/mistral-smoke.d.ts.map +1 -0
- package/dist/examples/mistral-smoke.js +63 -0
- package/dist/examples/onboarding-governance-smoke.d.ts +2 -0
- package/dist/examples/onboarding-governance-smoke.d.ts.map +1 -0
- package/dist/examples/onboarding-governance-smoke.js +477 -0
- package/dist/examples/onboarding-governance-tutorial.d.ts +2 -0
- package/dist/examples/onboarding-governance-tutorial.d.ts.map +1 -0
- package/dist/examples/onboarding-governance-tutorial.js +342 -0
- package/dist/examples/policy-smoke.d.ts +2 -0
- package/dist/examples/policy-smoke.d.ts.map +1 -0
- package/dist/examples/policy-smoke.js +137 -0
- package/dist/examples/support/scripted-provider.d.ts +8 -0
- package/dist/examples/support/scripted-provider.d.ts.map +1 -0
- package/dist/examples/support/scripted-provider.js +21 -0
- package/dist/examples/tool-policy-finance.d.ts +2 -0
- package/dist/examples/tool-policy-finance.d.ts.map +1 -0
- package/dist/examples/tool-policy-finance.js +77 -0
- package/dist/examples/tool-run.d.ts +2 -0
- package/dist/examples/tool-run.d.ts.map +1 -0
- package/dist/examples/tool-run.js +70 -0
- package/dist/guardrails.d.ts +20 -0
- package/dist/guardrails.d.ts.map +1 -0
- package/dist/guardrails.js +6 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +33 -0
- package/dist/logger.d.ts +76 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +53 -0
- package/dist/messages.d.ts +5 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +21 -0
- package/dist/policy.d.ts +41 -0
- package/dist/policy.d.ts.map +1 -0
- package/dist/policy.js +20 -0
- package/dist/provider-setup.d.ts +14 -0
- package/dist/provider-setup.d.ts.map +1 -0
- package/dist/provider-setup.js +43 -0
- package/dist/providers/base.d.ts +26 -0
- package/dist/providers/base.d.ts.map +1 -0
- package/dist/providers/base.js +2 -0
- package/dist/providers/chat-completions.d.ts +13 -0
- package/dist/providers/chat-completions.d.ts.map +1 -0
- package/dist/providers/chat-completions.js +314 -0
- package/dist/providers/mistral.d.ts +8 -0
- package/dist/providers/mistral.d.ts.map +1 -0
- package/dist/providers/mistral.js +14 -0
- package/dist/providers/openai.d.ts +11 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +23 -0
- package/dist/run-context.d.ts +5 -0
- package/dist/run-context.d.ts.map +1 -0
- package/dist/run-context.js +10 -0
- package/dist/run-log-emitter.d.ts +21 -0
- package/dist/run-log-emitter.d.ts.map +1 -0
- package/dist/run-log-emitter.js +184 -0
- package/dist/run-record.d.ts +61 -0
- package/dist/run-record.d.ts.map +1 -0
- package/dist/run-record.js +2 -0
- package/dist/run-recorder-runtime.d.ts +38 -0
- package/dist/run-recorder-runtime.d.ts.map +1 -0
- package/dist/run-recorder-runtime.js +148 -0
- package/dist/run.d.ts +19 -0
- package/dist/run.d.ts.map +1 -0
- package/dist/run.js +497 -0
- package/dist/tests/integration/chat-completions.integration.d.ts +2 -0
- package/dist/tests/integration/chat-completions.integration.d.ts.map +1 -0
- package/dist/tests/integration/chat-completions.integration.js +110 -0
- package/dist/tests/integration/index.d.ts +2 -0
- package/dist/tests/integration/index.d.ts.map +1 -0
- package/dist/tests/integration/index.js +12 -0
- package/dist/tests/regression/handoff-policy-trace.regression.d.ts +2 -0
- package/dist/tests/regression/handoff-policy-trace.regression.d.ts.map +1 -0
- package/dist/tests/regression/handoff-policy-trace.regression.js +92 -0
- package/dist/tests/regression/handoff-transition.regression.d.ts +2 -0
- package/dist/tests/regression/handoff-transition.regression.d.ts.map +1 -0
- package/dist/tests/regression/handoff-transition.regression.js +62 -0
- package/dist/tests/regression/index.d.ts +2 -0
- package/dist/tests/regression/index.d.ts.map +1 -0
- package/dist/tests/regression/index.js +14 -0
- package/dist/tests/regression/policy-default-deny.regression.d.ts +2 -0
- package/dist/tests/regression/policy-default-deny.regression.d.ts.map +1 -0
- package/dist/tests/regression/policy-default-deny.regression.js +106 -0
- package/dist/tests/support/handoff-name.d.ts +2 -0
- package/dist/tests/support/handoff-name.d.ts.map +1 -0
- package/dist/tests/support/handoff-name.js +14 -0
- package/dist/tests/support/scripted-provider.d.ts +8 -0
- package/dist/tests/support/scripted-provider.d.ts.map +1 -0
- package/dist/tests/support/scripted-provider.js +19 -0
- package/dist/tests/unit/guardrail.unit.d.ts +2 -0
- package/dist/tests/unit/guardrail.unit.d.ts.map +1 -0
- package/dist/tests/unit/guardrail.unit.js +48 -0
- package/dist/tests/unit/handoff.unit.d.ts +2 -0
- package/dist/tests/unit/handoff.unit.d.ts.map +1 -0
- package/dist/tests/unit/handoff.unit.js +178 -0
- package/dist/tests/unit/index.d.ts +2 -0
- package/dist/tests/unit/index.d.ts.map +1 -0
- package/dist/tests/unit/index.js +24 -0
- package/dist/tests/unit/logger.unit.d.ts +2 -0
- package/dist/tests/unit/logger.unit.d.ts.map +1 -0
- package/dist/tests/unit/logger.unit.js +65 -0
- package/dist/tests/unit/policy.unit.d.ts +2 -0
- package/dist/tests/unit/policy.unit.d.ts.map +1 -0
- package/dist/tests/unit/policy.unit.js +167 -0
- package/dist/tests/unit/provider-setup.unit.d.ts +2 -0
- package/dist/tests/unit/provider-setup.unit.d.ts.map +1 -0
- package/dist/tests/unit/provider-setup.unit.js +75 -0
- package/dist/tests/unit/run-record.unit.d.ts +2 -0
- package/dist/tests/unit/run-record.unit.d.ts.map +1 -0
- package/dist/tests/unit/run-record.unit.js +118 -0
- package/dist/tests/unit/run.unit.d.ts +2 -0
- package/dist/tests/unit/run.unit.d.ts.map +1 -0
- package/dist/tests/unit/run.unit.js +30 -0
- package/dist/tool.d.ts +16 -0
- package/dist/tool.d.ts.map +1 -0
- package/dist/tool.js +6 -0
- package/dist/types.d.ts +67 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/package.json +64 -0
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
7
|
+
const zod_1 = require("zod");
|
|
8
|
+
const index_1 = require("../index");
|
|
9
|
+
const POLICY_VERSION = "onboarding-policy.v1";
|
|
10
|
+
class SequencedProvider {
|
|
11
|
+
turns;
|
|
12
|
+
index = 0;
|
|
13
|
+
constructor(turns) {
|
|
14
|
+
this.turns = turns;
|
|
15
|
+
}
|
|
16
|
+
async *stream(_request) {
|
|
17
|
+
void _request;
|
|
18
|
+
const events = this.turns[this.index] ?? [];
|
|
19
|
+
this.index += 1;
|
|
20
|
+
for (const event of events) {
|
|
21
|
+
yield event;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function asObject(value) {
|
|
26
|
+
if (typeof value !== "object" || value === null) {
|
|
27
|
+
return {};
|
|
28
|
+
}
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
function asBoolean(value, fallback = false) {
|
|
32
|
+
return typeof value === "boolean" ? value : fallback;
|
|
33
|
+
}
|
|
34
|
+
function allow(reason, metadata) {
|
|
35
|
+
return {
|
|
36
|
+
decision: "allow",
|
|
37
|
+
reason,
|
|
38
|
+
policyVersion: POLICY_VERSION,
|
|
39
|
+
metadata,
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
function deny(reason, metadata) {
|
|
43
|
+
return {
|
|
44
|
+
decision: "deny",
|
|
45
|
+
reason,
|
|
46
|
+
policyVersion: POLICY_VERSION,
|
|
47
|
+
metadata,
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
function createPolicies() {
|
|
51
|
+
const toolPolicy = ({ toolName, parsedArguments, runContext, }) => {
|
|
52
|
+
const context = runContext.context;
|
|
53
|
+
const args = asObject(parsedArguments);
|
|
54
|
+
const privileged = asBoolean(args.privileged);
|
|
55
|
+
if (toolName === "create_corporate_identity") {
|
|
56
|
+
if (!context.actor.groups.includes("onboarding-admin")) {
|
|
57
|
+
return deny("deny_missing_onboarding_admin_group");
|
|
58
|
+
}
|
|
59
|
+
return allow("allow_onboarding_admin_identity_creation");
|
|
60
|
+
}
|
|
61
|
+
if (toolName === "activate_access") {
|
|
62
|
+
if (!context.compliance.mandatoryTrainingCompleted) {
|
|
63
|
+
return deny("deny_missing_compliance_training");
|
|
64
|
+
}
|
|
65
|
+
if (privileged && !context.approvals.securityApproved) {
|
|
66
|
+
return deny("deny_missing_security_approval");
|
|
67
|
+
}
|
|
68
|
+
return allow("allow_access_activation_requirements_met", {
|
|
69
|
+
privileged,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
return deny("deny_tool_not_allowlisted");
|
|
73
|
+
};
|
|
74
|
+
const handoffPolicy = ({ fromAgentName, toAgentName, handoffPayload, runContext, }) => {
|
|
75
|
+
const context = runContext.context;
|
|
76
|
+
const payload = asObject(handoffPayload);
|
|
77
|
+
const privileged = asBoolean(payload.privileged);
|
|
78
|
+
if (fromAgentName !== "Onboarding Coordinator" ||
|
|
79
|
+
toAgentName !== "Security Agent") {
|
|
80
|
+
return deny("deny_handoff_not_allowlisted");
|
|
81
|
+
}
|
|
82
|
+
if (privileged && !context.approvals.managerApproved) {
|
|
83
|
+
return deny("deny_missing_manager_approval");
|
|
84
|
+
}
|
|
85
|
+
if (privileged && !context.approvals.securityApproved) {
|
|
86
|
+
return deny("deny_missing_security_approval");
|
|
87
|
+
}
|
|
88
|
+
return allow("allow_privileged_escalation_path", {
|
|
89
|
+
privileged,
|
|
90
|
+
});
|
|
91
|
+
};
|
|
92
|
+
return {
|
|
93
|
+
toolPolicy,
|
|
94
|
+
handoffPolicy,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
function maskEmail(email) {
|
|
98
|
+
const [local, domain] = email.split("@");
|
|
99
|
+
if (!domain) {
|
|
100
|
+
return "[REDACTED]";
|
|
101
|
+
}
|
|
102
|
+
if (local.length < 2) {
|
|
103
|
+
return `*@${domain}`;
|
|
104
|
+
}
|
|
105
|
+
return `${local[0]}***${local[local.length - 1]}@${domain}`;
|
|
106
|
+
}
|
|
107
|
+
function redactContext(context) {
|
|
108
|
+
return {
|
|
109
|
+
contextSnapshot: {
|
|
110
|
+
...context,
|
|
111
|
+
actor: {
|
|
112
|
+
...context.actor,
|
|
113
|
+
email: maskEmail(context.actor.email),
|
|
114
|
+
},
|
|
115
|
+
pii: {
|
|
116
|
+
taxId: "[REDACTED]",
|
|
117
|
+
personalPhone: "[REDACTED]",
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
contextRedacted: true,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function createContext(input) {
|
|
124
|
+
return {
|
|
125
|
+
tenantId: "corp-acme",
|
|
126
|
+
employeeId: input.employeeId,
|
|
127
|
+
actor: {
|
|
128
|
+
userId: "hr-ops-01",
|
|
129
|
+
groups: [...input.groups],
|
|
130
|
+
email: "hr.ops@corp.example",
|
|
131
|
+
},
|
|
132
|
+
compliance: {
|
|
133
|
+
mandatoryTrainingCompleted: input.mandatoryTrainingCompleted,
|
|
134
|
+
},
|
|
135
|
+
approvals: {
|
|
136
|
+
managerApproved: input.managerApproved,
|
|
137
|
+
securityApproved: input.securityApproved,
|
|
138
|
+
},
|
|
139
|
+
runtimeState: {
|
|
140
|
+
identityCreated: false,
|
|
141
|
+
accessActivated: false,
|
|
142
|
+
},
|
|
143
|
+
pii: {
|
|
144
|
+
taxId: "AAABBB99C99Z999Z",
|
|
145
|
+
personalPhone: "+39-555-123-456",
|
|
146
|
+
},
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function createAgentGraph() {
|
|
150
|
+
const createCorporateIdentity = (0, index_1.tool)({
|
|
151
|
+
name: "create_corporate_identity",
|
|
152
|
+
description: "Create identity in enterprise IAM.",
|
|
153
|
+
parameters: zod_1.z.object({}),
|
|
154
|
+
execute: (_input, runContext) => {
|
|
155
|
+
if (runContext) {
|
|
156
|
+
runContext.context.runtimeState.identityCreated = true;
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
identityCreated: true,
|
|
160
|
+
};
|
|
161
|
+
},
|
|
162
|
+
});
|
|
163
|
+
const activateAccess = (0, index_1.tool)({
|
|
164
|
+
name: "activate_access",
|
|
165
|
+
description: "Activate application access.",
|
|
166
|
+
parameters: zod_1.z.object({
|
|
167
|
+
app: zod_1.z.string(),
|
|
168
|
+
privileged: zod_1.z.boolean().default(false),
|
|
169
|
+
}),
|
|
170
|
+
execute: (input, runContext) => {
|
|
171
|
+
if (runContext) {
|
|
172
|
+
runContext.context.runtimeState.accessActivated = true;
|
|
173
|
+
}
|
|
174
|
+
return {
|
|
175
|
+
activated: true,
|
|
176
|
+
app: input.app,
|
|
177
|
+
privileged: input.privileged,
|
|
178
|
+
};
|
|
179
|
+
},
|
|
180
|
+
});
|
|
181
|
+
const securityAgent = new index_1.Agent({
|
|
182
|
+
name: "Security Agent",
|
|
183
|
+
model: "fake-model",
|
|
184
|
+
tools: [activateAccess],
|
|
185
|
+
});
|
|
186
|
+
return new index_1.Agent({
|
|
187
|
+
name: "Onboarding Coordinator",
|
|
188
|
+
model: "fake-model",
|
|
189
|
+
tools: [createCorporateIdentity, activateAccess],
|
|
190
|
+
handoffs: [securityAgent],
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
function createHappyPathTurns() {
|
|
194
|
+
return [
|
|
195
|
+
[
|
|
196
|
+
{
|
|
197
|
+
type: "tool_call",
|
|
198
|
+
callId: "call-1",
|
|
199
|
+
name: "create_corporate_identity",
|
|
200
|
+
arguments: "{}",
|
|
201
|
+
},
|
|
202
|
+
],
|
|
203
|
+
[
|
|
204
|
+
{
|
|
205
|
+
type: "tool_call",
|
|
206
|
+
callId: "call-2",
|
|
207
|
+
name: "handoff_to_security_agent",
|
|
208
|
+
arguments: JSON.stringify({
|
|
209
|
+
privileged: true,
|
|
210
|
+
app: "ERP_FINANCE",
|
|
211
|
+
}),
|
|
212
|
+
},
|
|
213
|
+
],
|
|
214
|
+
[
|
|
215
|
+
{
|
|
216
|
+
type: "tool_call",
|
|
217
|
+
callId: "call-3",
|
|
218
|
+
name: "activate_access",
|
|
219
|
+
arguments: JSON.stringify({
|
|
220
|
+
privileged: true,
|
|
221
|
+
app: "ERP_FINANCE",
|
|
222
|
+
}),
|
|
223
|
+
},
|
|
224
|
+
],
|
|
225
|
+
[
|
|
226
|
+
{
|
|
227
|
+
type: "completed",
|
|
228
|
+
message: "Onboarding completed: identity and privileged access are active.",
|
|
229
|
+
},
|
|
230
|
+
],
|
|
231
|
+
];
|
|
232
|
+
}
|
|
233
|
+
function createHandoffDenyTurns() {
|
|
234
|
+
return [
|
|
235
|
+
[
|
|
236
|
+
{
|
|
237
|
+
type: "tool_call",
|
|
238
|
+
callId: "call-1",
|
|
239
|
+
name: "create_corporate_identity",
|
|
240
|
+
arguments: "{}",
|
|
241
|
+
},
|
|
242
|
+
],
|
|
243
|
+
[
|
|
244
|
+
{
|
|
245
|
+
type: "tool_call",
|
|
246
|
+
callId: "call-2",
|
|
247
|
+
name: "handoff_to_security_agent",
|
|
248
|
+
arguments: JSON.stringify({
|
|
249
|
+
privileged: true,
|
|
250
|
+
app: "ERP_FINANCE",
|
|
251
|
+
}),
|
|
252
|
+
},
|
|
253
|
+
],
|
|
254
|
+
];
|
|
255
|
+
}
|
|
256
|
+
function printRunRecord(label, record) {
|
|
257
|
+
process.stdout.write(`\n=== ${label} ===\n`);
|
|
258
|
+
process.stdout.write(`status: ${record.status}\n`);
|
|
259
|
+
process.stdout.write(`agentName: ${record.agentName}\n`);
|
|
260
|
+
process.stdout.write(`question: ${record.question}\n`);
|
|
261
|
+
process.stdout.write(`response: ${record.response}\n`);
|
|
262
|
+
process.stdout.write(`contextRedacted: ${String(record.contextRedacted)}\n`);
|
|
263
|
+
process.stdout.write(`redactedTaxId: ${record.contextSnapshot.pii.taxId}\n`);
|
|
264
|
+
process.stdout.write("policyDecisions:\n");
|
|
265
|
+
for (const decision of record.policyDecisions) {
|
|
266
|
+
process.stdout.write(`- turn=${decision.turn} resource=${decision.resource.kind}:${decision.resource.name} decision=${decision.decision} reason=${decision.reason} policyVersion=${decision.policyVersion ?? "n/a"}\n`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
async function runScenario(input) {
|
|
270
|
+
const records = [];
|
|
271
|
+
const policies = createPolicies();
|
|
272
|
+
const agent = createAgentGraph();
|
|
273
|
+
(0, index_1.setDefaultProvider)(new SequencedProvider(input.turns));
|
|
274
|
+
try {
|
|
275
|
+
const result = await (0, index_1.run)(agent, `Onboard employee ${input.context.employeeId} for privileged ERP access.`, {
|
|
276
|
+
context: input.context,
|
|
277
|
+
policies,
|
|
278
|
+
record: {
|
|
279
|
+
metadata: {
|
|
280
|
+
scenario: input.label,
|
|
281
|
+
},
|
|
282
|
+
contextRedactor: redactContext,
|
|
283
|
+
sink: (record) => {
|
|
284
|
+
records.push(record);
|
|
285
|
+
},
|
|
286
|
+
},
|
|
287
|
+
});
|
|
288
|
+
process.stdout.write(`\n[${input.label}] result: ${result.finalOutput}\n`);
|
|
289
|
+
}
|
|
290
|
+
catch (error) {
|
|
291
|
+
if (!(error instanceof index_1.HandoffPolicyDeniedError)) {
|
|
292
|
+
throw error;
|
|
293
|
+
}
|
|
294
|
+
process.stdout.write(`\n[${input.label}] failed: ${error.result.policyResult.reason}\n`);
|
|
295
|
+
}
|
|
296
|
+
strict_1.default.equal(records.length, 1);
|
|
297
|
+
return {
|
|
298
|
+
outcome: records[0]?.status ?? "failed",
|
|
299
|
+
record: records[0],
|
|
300
|
+
};
|
|
301
|
+
}
|
|
302
|
+
async function main() {
|
|
303
|
+
const t0 = await runScenario({
|
|
304
|
+
label: "t0_allow",
|
|
305
|
+
context: createContext({
|
|
306
|
+
employeeId: "E-200",
|
|
307
|
+
groups: ["onboarding-admin"],
|
|
308
|
+
mandatoryTrainingCompleted: true,
|
|
309
|
+
managerApproved: true,
|
|
310
|
+
securityApproved: true,
|
|
311
|
+
}),
|
|
312
|
+
turns: createHappyPathTurns(),
|
|
313
|
+
});
|
|
314
|
+
const t1 = await runScenario({
|
|
315
|
+
label: "t1_deny",
|
|
316
|
+
context: createContext({
|
|
317
|
+
employeeId: "E-200",
|
|
318
|
+
groups: ["onboarding-admin"],
|
|
319
|
+
mandatoryTrainingCompleted: true,
|
|
320
|
+
managerApproved: true,
|
|
321
|
+
securityApproved: false,
|
|
322
|
+
}),
|
|
323
|
+
turns: createHandoffDenyTurns(),
|
|
324
|
+
});
|
|
325
|
+
printRunRecord("RunRecord @ t0 (allow)", t0.record);
|
|
326
|
+
printRunRecord("RunRecord @ t1 (deny)", t1.record);
|
|
327
|
+
const t0HandoffDecision = t0.record.policyDecisions.find((decision) => decision.resource.kind === "handoff");
|
|
328
|
+
const t1HandoffDecision = t1.record.policyDecisions.find((decision) => decision.resource.kind === "handoff");
|
|
329
|
+
strict_1.default.equal(t0.outcome, "completed");
|
|
330
|
+
strict_1.default.equal(t1.outcome, "failed");
|
|
331
|
+
strict_1.default.equal(t0HandoffDecision?.reason, "allow_privileged_escalation_path");
|
|
332
|
+
strict_1.default.equal(t1HandoffDecision?.reason, "deny_missing_security_approval");
|
|
333
|
+
process.stdout.write("\n=== Comparison ===\n");
|
|
334
|
+
process.stdout.write(`t0 handoff reason: ${t0HandoffDecision?.reason ?? "n/a"}\n`);
|
|
335
|
+
process.stdout.write(`t1 handoff reason: ${t1HandoffDecision?.reason ?? "n/a"}\n`);
|
|
336
|
+
process.stdout.write("Tutorial completed.\n");
|
|
337
|
+
}
|
|
338
|
+
main().catch((error) => {
|
|
339
|
+
const message = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
340
|
+
process.stderr.write(`${message}\n`);
|
|
341
|
+
process.exit(1);
|
|
342
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"policy-smoke.d.ts","sourceRoot":"","sources":["../../src/examples/policy-smoke.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const strict_1 = __importDefault(require("node:assert/strict"));
|
|
7
|
+
const zod_1 = require("zod");
|
|
8
|
+
const index_1 = require("../index");
|
|
9
|
+
class SequencedProvider {
|
|
10
|
+
turns;
|
|
11
|
+
index = 0;
|
|
12
|
+
constructor(turns) {
|
|
13
|
+
this.turns = turns;
|
|
14
|
+
}
|
|
15
|
+
async *stream(_request) {
|
|
16
|
+
void _request;
|
|
17
|
+
const events = this.turns[this.index] ?? [];
|
|
18
|
+
this.index += 1;
|
|
19
|
+
for (const event of events) {
|
|
20
|
+
yield event;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function createAgent(onExecute) {
|
|
25
|
+
const ping = (0, index_1.tool)({
|
|
26
|
+
name: "ping",
|
|
27
|
+
description: "Simple test tool",
|
|
28
|
+
parameters: zod_1.z.object({}),
|
|
29
|
+
execute: () => {
|
|
30
|
+
onExecute();
|
|
31
|
+
return { ok: true };
|
|
32
|
+
},
|
|
33
|
+
});
|
|
34
|
+
return new index_1.Agent({
|
|
35
|
+
name: "Policy smoke agent",
|
|
36
|
+
model: "fake-model",
|
|
37
|
+
tools: [ping],
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
function createToolProposalRun() {
|
|
41
|
+
return [
|
|
42
|
+
[
|
|
43
|
+
{
|
|
44
|
+
type: "tool_call",
|
|
45
|
+
callId: "call-1",
|
|
46
|
+
name: "ping",
|
|
47
|
+
arguments: "{}",
|
|
48
|
+
},
|
|
49
|
+
],
|
|
50
|
+
[{ type: "completed", message: "done" }],
|
|
51
|
+
];
|
|
52
|
+
}
|
|
53
|
+
async function runAllowCase() {
|
|
54
|
+
let executions = 0;
|
|
55
|
+
(0, index_1.setDefaultProvider)(new SequencedProvider(createToolProposalRun()));
|
|
56
|
+
const toolPolicy = () => (0, index_1.allow)("allow_ping");
|
|
57
|
+
const result = await (0, index_1.run)(createAgent(() => (executions += 1)), "hello", {
|
|
58
|
+
policies: {
|
|
59
|
+
toolPolicy,
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
strict_1.default.equal(result.finalOutput, "done");
|
|
63
|
+
strict_1.default.equal(executions, 1);
|
|
64
|
+
}
|
|
65
|
+
async function runMissingPolicyCase() {
|
|
66
|
+
let executions = 0;
|
|
67
|
+
(0, index_1.setDefaultProvider)(new SequencedProvider(createToolProposalRun()));
|
|
68
|
+
await strict_1.default.rejects(() => (0, index_1.run)(createAgent(() => (executions += 1)), "hello"), (error) => {
|
|
69
|
+
strict_1.default.ok(error instanceof index_1.ToolCallPolicyDeniedError);
|
|
70
|
+
strict_1.default.equal(error.result.policyResult.reason, "policy_not_configured");
|
|
71
|
+
return true;
|
|
72
|
+
});
|
|
73
|
+
strict_1.default.equal(executions, 0);
|
|
74
|
+
}
|
|
75
|
+
async function runDenyCase() {
|
|
76
|
+
let executions = 0;
|
|
77
|
+
(0, index_1.setDefaultProvider)(new SequencedProvider(createToolProposalRun()));
|
|
78
|
+
const toolPolicy = () => (0, index_1.deny)("tool_not_allowlisted");
|
|
79
|
+
await strict_1.default.rejects(() => (0, index_1.run)(createAgent(() => (executions += 1)), "hello", {
|
|
80
|
+
policies: {
|
|
81
|
+
toolPolicy,
|
|
82
|
+
},
|
|
83
|
+
}), (error) => {
|
|
84
|
+
strict_1.default.ok(error instanceof index_1.ToolCallPolicyDeniedError);
|
|
85
|
+
strict_1.default.equal(error.result.policyResult.reason, "tool_not_allowlisted");
|
|
86
|
+
return true;
|
|
87
|
+
});
|
|
88
|
+
strict_1.default.equal(executions, 0);
|
|
89
|
+
}
|
|
90
|
+
async function runPolicyErrorCase() {
|
|
91
|
+
let executions = 0;
|
|
92
|
+
(0, index_1.setDefaultProvider)(new SequencedProvider(createToolProposalRun()));
|
|
93
|
+
const toolPolicy = () => {
|
|
94
|
+
throw new Error("policy exploded");
|
|
95
|
+
};
|
|
96
|
+
await strict_1.default.rejects(() => (0, index_1.run)(createAgent(() => (executions += 1)), "hello", {
|
|
97
|
+
policies: {
|
|
98
|
+
toolPolicy,
|
|
99
|
+
},
|
|
100
|
+
}), (error) => {
|
|
101
|
+
strict_1.default.ok(error instanceof index_1.ToolCallPolicyDeniedError);
|
|
102
|
+
strict_1.default.equal(error.result.policyResult.reason, "policy_error");
|
|
103
|
+
strict_1.default.equal(error.result.policyResult.metadata?.errorName, "Error");
|
|
104
|
+
return true;
|
|
105
|
+
});
|
|
106
|
+
strict_1.default.equal(executions, 0);
|
|
107
|
+
}
|
|
108
|
+
async function runInvalidPolicyResultCase() {
|
|
109
|
+
let executions = 0;
|
|
110
|
+
(0, index_1.setDefaultProvider)(new SequencedProvider(createToolProposalRun()));
|
|
111
|
+
const toolPolicy = (() => ({
|
|
112
|
+
decision: "allow",
|
|
113
|
+
}));
|
|
114
|
+
await strict_1.default.rejects(() => (0, index_1.run)(createAgent(() => (executions += 1)), "hello", {
|
|
115
|
+
policies: {
|
|
116
|
+
toolPolicy,
|
|
117
|
+
},
|
|
118
|
+
}), (error) => {
|
|
119
|
+
strict_1.default.ok(error instanceof index_1.ToolCallPolicyDeniedError);
|
|
120
|
+
strict_1.default.equal(error.result.policyResult.reason, "invalid_policy_result");
|
|
121
|
+
return true;
|
|
122
|
+
});
|
|
123
|
+
strict_1.default.equal(executions, 0);
|
|
124
|
+
}
|
|
125
|
+
async function main() {
|
|
126
|
+
await runAllowCase();
|
|
127
|
+
await runMissingPolicyCase();
|
|
128
|
+
await runDenyCase();
|
|
129
|
+
await runPolicyErrorCase();
|
|
130
|
+
await runInvalidPolicyResultCase();
|
|
131
|
+
process.stdout.write("Policy smoke passed.\n");
|
|
132
|
+
}
|
|
133
|
+
main().catch((error) => {
|
|
134
|
+
const message = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
135
|
+
process.stderr.write(`${message}\n`);
|
|
136
|
+
process.exit(1);
|
|
137
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ModelProvider, ProviderEvent, ProviderRequest } from "../../providers/base";
|
|
2
|
+
export declare class ScriptedProvider implements ModelProvider {
|
|
3
|
+
private readonly turns;
|
|
4
|
+
private index;
|
|
5
|
+
constructor(turns: ProviderEvent[][]);
|
|
6
|
+
stream<TContext = unknown>(_request: ProviderRequest<TContext>): AsyncIterable<ProviderEvent>;
|
|
7
|
+
}
|
|
8
|
+
//# sourceMappingURL=scripted-provider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scripted-provider.d.ts","sourceRoot":"","sources":["../../../src/examples/support/scripted-provider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,aAAa,EACb,aAAa,EACb,eAAe,EAChB,MAAM,sBAAsB,CAAC;AAI9B,qBAAa,gBAAiB,YAAW,aAAa;IACpD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAoB;IAC1C,OAAO,CAAC,KAAK,CAAK;gBAEN,KAAK,EAAE,aAAa,EAAE,EAAE;IAI7B,MAAM,CAAC,QAAQ,GAAG,OAAO,EAC9B,QAAQ,EAAE,eAAe,CAAC,QAAQ,CAAC,GAClC,aAAa,CAAC,aAAa,CAAC;CAShC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ScriptedProvider = void 0;
|
|
4
|
+
// Small deterministic provider used by examples:
|
|
5
|
+
// each "turn" is pre-scripted, so behavior is predictable and easy to learn.
|
|
6
|
+
class ScriptedProvider {
|
|
7
|
+
turns;
|
|
8
|
+
index = 0;
|
|
9
|
+
constructor(turns) {
|
|
10
|
+
this.turns = turns;
|
|
11
|
+
}
|
|
12
|
+
async *stream(_request) {
|
|
13
|
+
void _request;
|
|
14
|
+
const events = this.turns[this.index] ?? [];
|
|
15
|
+
this.index += 1;
|
|
16
|
+
for (const event of events) {
|
|
17
|
+
yield event;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
exports.ScriptedProvider = ScriptedProvider;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-policy-finance.d.ts","sourceRoot":"","sources":["../../src/examples/tool-policy-finance.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC"}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
require("dotenv/config");
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const index_1 = require("../index");
|
|
6
|
+
async function main() {
|
|
7
|
+
// Minimal provider setup from env (MISTRAL_API_KEY).
|
|
8
|
+
(0, index_1.setupMistral)();
|
|
9
|
+
// One tool: read a finance report by id.
|
|
10
|
+
const getFinanceReport = (0, index_1.tool)({
|
|
11
|
+
name: "get_finance_report",
|
|
12
|
+
description: "Return summary fields for a finance report.",
|
|
13
|
+
parameters: zod_1.z.object({
|
|
14
|
+
reportId: zod_1.z.string(),
|
|
15
|
+
}),
|
|
16
|
+
execute: async ({ reportId }) => {
|
|
17
|
+
return {
|
|
18
|
+
reportId,
|
|
19
|
+
revenue: 1240000,
|
|
20
|
+
costs: 820000,
|
|
21
|
+
margin: 420000,
|
|
22
|
+
currency: "EUR",
|
|
23
|
+
};
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
const agent = new index_1.Agent({
|
|
27
|
+
name: "Finance Analyst Agent",
|
|
28
|
+
model: "mistral-small-latest",
|
|
29
|
+
instructions: "If asked about a finance report, call get_finance_report first, then provide a short business summary.",
|
|
30
|
+
tools: [getFinanceReport],
|
|
31
|
+
});
|
|
32
|
+
// Policy gate: the tool is allowed only to users in 'finance' group.
|
|
33
|
+
const toolPolicy = ({ runContext }) => {
|
|
34
|
+
const hasFinanceAccess = runContext.context.actor.groups.includes("finance");
|
|
35
|
+
if (!hasFinanceAccess) {
|
|
36
|
+
return (0, index_1.deny)("deny_missing_finance_group", {
|
|
37
|
+
policyVersion: "finance-policy.v1",
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return (0, index_1.allow)("allow_finance_group_access", {
|
|
41
|
+
policyVersion: "finance-policy.v1",
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
// Optional logger: prints only the policy decision event to keep output readable.
|
|
45
|
+
const logger = (0, index_1.createStdoutLogger)({
|
|
46
|
+
pretty: true,
|
|
47
|
+
events: ["tool_policy_evaluated"],
|
|
48
|
+
});
|
|
49
|
+
const stream = await (0, index_1.run)(agent, "Give me a concise summary for report Q1-2026.", {
|
|
50
|
+
stream: true,
|
|
51
|
+
context: {
|
|
52
|
+
actor: {
|
|
53
|
+
userId: "u-42",
|
|
54
|
+
groups: ["finance"],
|
|
55
|
+
},
|
|
56
|
+
},
|
|
57
|
+
policies: {
|
|
58
|
+
toolPolicy,
|
|
59
|
+
},
|
|
60
|
+
logger,
|
|
61
|
+
maxTurns: 6,
|
|
62
|
+
});
|
|
63
|
+
for await (const event of stream.toStream()) {
|
|
64
|
+
if (event.type === "run_item_stream_event" &&
|
|
65
|
+
event.item.type === "tool_call_item") {
|
|
66
|
+
process.stdout.write(`\n[tool call] ${event.item.name} ${JSON.stringify(event.item.arguments)}\n\n`);
|
|
67
|
+
}
|
|
68
|
+
if (event.type === "raw_model_stream_event") {
|
|
69
|
+
process.stdout.write(event.data.delta ?? "");
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
process.stdout.write(`\n\nCompleted. Last agent: ${stream.lastAgent.name}. History items: ${stream.history.length}\n`);
|
|
73
|
+
}
|
|
74
|
+
main().catch((error) => {
|
|
75
|
+
process.stderr.write(`${String(error)}\n`);
|
|
76
|
+
process.exit(1);
|
|
77
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tool-run.d.ts","sourceRoot":"","sources":["../../src/examples/tool-run.ts"],"names":[],"mappings":"AAAA,OAAO,eAAe,CAAC"}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
require("dotenv/config");
|
|
4
|
+
const zod_1 = require("zod");
|
|
5
|
+
const index_1 = require("../index");
|
|
6
|
+
async function main() {
|
|
7
|
+
// Minimal provider setup from env (MISTRAL_API_KEY).
|
|
8
|
+
(0, index_1.setupMistral)();
|
|
9
|
+
// One tool: read a finance report by id.
|
|
10
|
+
const getFinanceReport = (0, index_1.tool)({
|
|
11
|
+
name: "get_finance_report",
|
|
12
|
+
description: "Return summary fields for a finance report.",
|
|
13
|
+
parameters: zod_1.z.object({
|
|
14
|
+
reportId: zod_1.z.string(),
|
|
15
|
+
}),
|
|
16
|
+
execute: async ({ reportId }) => {
|
|
17
|
+
return {
|
|
18
|
+
reportId,
|
|
19
|
+
revenue: 1240000,
|
|
20
|
+
costs: 820000,
|
|
21
|
+
margin: 420000,
|
|
22
|
+
currency: "EUR",
|
|
23
|
+
};
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
const agent = new index_1.Agent({
|
|
27
|
+
name: "Finance Analyst Agent",
|
|
28
|
+
model: "mistral-small-latest",
|
|
29
|
+
instructions: "If asked about a finance report, call get_finance_report first, then provide a short business summary.",
|
|
30
|
+
tools: [getFinanceReport],
|
|
31
|
+
});
|
|
32
|
+
// Policy gate: the tool is allowed only to users in 'finance' group.
|
|
33
|
+
const toolPolicy = ({ runContext }) => {
|
|
34
|
+
const hasFinanceAccess = runContext.context.actor.groups.includes("finance");
|
|
35
|
+
if (!hasFinanceAccess) {
|
|
36
|
+
return (0, index_1.deny)("deny_missing_finance_group", {
|
|
37
|
+
policyVersion: "finance-policy.v1",
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
return (0, index_1.allow)("allow_finance_group_access", {
|
|
41
|
+
policyVersion: "finance-policy.v1",
|
|
42
|
+
});
|
|
43
|
+
};
|
|
44
|
+
const stream = await (0, index_1.run)(agent, "Give me a concise summary for report Q1-2026.", {
|
|
45
|
+
stream: true,
|
|
46
|
+
context: {
|
|
47
|
+
actor: {
|
|
48
|
+
userId: "u-42",
|
|
49
|
+
groups: ["finance"],
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
policies: {
|
|
53
|
+
toolPolicy,
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
for await (const event of stream.toStream()) {
|
|
57
|
+
if (event.type === "run_item_stream_event" &&
|
|
58
|
+
event.item.type === "tool_call_item") {
|
|
59
|
+
process.stdout.write(`\n[tool call] ${event.item.name} ${JSON.stringify(event.item.arguments)}\n\n`);
|
|
60
|
+
}
|
|
61
|
+
if (event.type === "raw_model_stream_event") {
|
|
62
|
+
process.stdout.write(event.data.delta ?? "");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
process.stdout.write(`\n\nCompleted. Last agent: ${stream.lastAgent.name}. History items: ${stream.history.length}\n`);
|
|
66
|
+
}
|
|
67
|
+
main().catch((error) => {
|
|
68
|
+
process.stderr.write(`${String(error)}\n`);
|
|
69
|
+
process.exit(1);
|
|
70
|
+
});
|