@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,477 @@
|
|
|
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
|
+
// Deterministic provider used to script each turn without relying on an external LLM.
|
|
11
|
+
class SequencedProvider {
|
|
12
|
+
turns;
|
|
13
|
+
index = 0;
|
|
14
|
+
constructor(turns) {
|
|
15
|
+
this.turns = turns;
|
|
16
|
+
}
|
|
17
|
+
async *stream(_request) {
|
|
18
|
+
void _request;
|
|
19
|
+
const events = this.turns[this.index] ?? [];
|
|
20
|
+
this.index += 1;
|
|
21
|
+
for (const event of events) {
|
|
22
|
+
yield event;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
function asObject(value) {
|
|
27
|
+
if (typeof value !== "object" || value === null) {
|
|
28
|
+
return {};
|
|
29
|
+
}
|
|
30
|
+
return value;
|
|
31
|
+
}
|
|
32
|
+
function asString(value, fallback = "") {
|
|
33
|
+
return typeof value === "string" ? value : fallback;
|
|
34
|
+
}
|
|
35
|
+
function asBoolean(value, fallback = false) {
|
|
36
|
+
return typeof value === "boolean" ? value : fallback;
|
|
37
|
+
}
|
|
38
|
+
function allow(reason, metadata) {
|
|
39
|
+
return (0, index_1.allow)(reason, {
|
|
40
|
+
policyVersion: POLICY_VERSION,
|
|
41
|
+
metadata,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
function deny(reason, metadata) {
|
|
45
|
+
return (0, index_1.deny)(reason, {
|
|
46
|
+
policyVersion: POLICY_VERSION,
|
|
47
|
+
metadata,
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
function createPolicies() {
|
|
51
|
+
// Tool policy is the enforcement point: only explicit allow can execute tools.
|
|
52
|
+
const toolPolicy = ({ toolName, parsedArguments, runContext, }) => {
|
|
53
|
+
const context = runContext.context;
|
|
54
|
+
const args = asObject(parsedArguments);
|
|
55
|
+
const app = asString(args.app);
|
|
56
|
+
const privileged = asBoolean(args.privileged);
|
|
57
|
+
if (toolName === "create_corporate_identity") {
|
|
58
|
+
if (!context.actor.groups.includes("onboarding-admin")) {
|
|
59
|
+
return deny("deny_missing_onboarding_admin_group");
|
|
60
|
+
}
|
|
61
|
+
return allow("allow_onboarding_admin_identity_creation");
|
|
62
|
+
}
|
|
63
|
+
if (toolName === "activate_access") {
|
|
64
|
+
if (!context.compliance.mandatoryTrainingCompleted) {
|
|
65
|
+
return deny("deny_missing_compliance_training", {
|
|
66
|
+
requiredTraining: "SEC-101",
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
if (privileged && !context.approvals.securityApproved) {
|
|
70
|
+
return deny("deny_missing_security_approval");
|
|
71
|
+
}
|
|
72
|
+
return allow("allow_access_activation_requirements_met", {
|
|
73
|
+
app,
|
|
74
|
+
privileged,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if (toolName === "request_app_access") {
|
|
78
|
+
if (app.startsWith("CRM") && !context.actor.groups.includes("sales")) {
|
|
79
|
+
return deny("deny_actor_group_mismatch", {
|
|
80
|
+
requiredGroup: "sales",
|
|
81
|
+
app,
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return allow("allow_department_scope_match", {
|
|
85
|
+
app,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
return deny("deny_tool_not_allowlisted");
|
|
89
|
+
};
|
|
90
|
+
// Handoff policy separately controls inter-agent transitions.
|
|
91
|
+
const handoffPolicy = ({ fromAgentName, toAgentName, handoffPayload, runContext, }) => {
|
|
92
|
+
const context = runContext.context;
|
|
93
|
+
const payload = asObject(handoffPayload);
|
|
94
|
+
const privileged = asBoolean(payload.privileged);
|
|
95
|
+
if (fromAgentName !== "Onboarding Coordinator" ||
|
|
96
|
+
toAgentName !== "Security Agent") {
|
|
97
|
+
return deny("deny_handoff_not_allowlisted");
|
|
98
|
+
}
|
|
99
|
+
if (privileged && !context.approvals.managerApproved) {
|
|
100
|
+
return deny("deny_missing_manager_approval");
|
|
101
|
+
}
|
|
102
|
+
if (privileged && !context.approvals.securityApproved) {
|
|
103
|
+
return deny("deny_missing_security_approval");
|
|
104
|
+
}
|
|
105
|
+
return allow("allow_privileged_escalation_path", {
|
|
106
|
+
privileged,
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
return {
|
|
110
|
+
toolPolicy,
|
|
111
|
+
handoffPolicy,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
function maskEmail(email) {
|
|
115
|
+
const [local, domain] = email.split("@");
|
|
116
|
+
if (!domain) {
|
|
117
|
+
return "[REDACTED]";
|
|
118
|
+
}
|
|
119
|
+
if (local.length < 2) {
|
|
120
|
+
return `*@${domain}`;
|
|
121
|
+
}
|
|
122
|
+
return `${local[0]}***${local[local.length - 1]}@${domain}`;
|
|
123
|
+
}
|
|
124
|
+
function redactContext(context) {
|
|
125
|
+
// Persist only sanitized data in RunRecord snapshots.
|
|
126
|
+
return {
|
|
127
|
+
contextSnapshot: {
|
|
128
|
+
...context,
|
|
129
|
+
actor: {
|
|
130
|
+
...context.actor,
|
|
131
|
+
email: maskEmail(context.actor.email),
|
|
132
|
+
},
|
|
133
|
+
pii: {
|
|
134
|
+
taxId: "[REDACTED]",
|
|
135
|
+
personalPhone: "[REDACTED]",
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
contextRedacted: true,
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function createContext(input) {
|
|
142
|
+
return {
|
|
143
|
+
tenantId: "corp-acme",
|
|
144
|
+
employeeId: input.employeeId,
|
|
145
|
+
requestedRole: input.requestedRole,
|
|
146
|
+
actor: {
|
|
147
|
+
userId: "hr-ops-01",
|
|
148
|
+
department: input.actorDepartment,
|
|
149
|
+
groups: [...input.actorGroups],
|
|
150
|
+
email: "hr.ops@corp.example",
|
|
151
|
+
},
|
|
152
|
+
compliance: {
|
|
153
|
+
mandatoryTrainingCompleted: input.mandatoryTrainingCompleted,
|
|
154
|
+
},
|
|
155
|
+
approvals: {
|
|
156
|
+
managerApproved: input.managerApproved,
|
|
157
|
+
securityApproved: input.securityApproved,
|
|
158
|
+
},
|
|
159
|
+
runtimeState: {
|
|
160
|
+
identityCreated: false,
|
|
161
|
+
accessActivated: false,
|
|
162
|
+
accessRequests: [],
|
|
163
|
+
},
|
|
164
|
+
pii: {
|
|
165
|
+
taxId: "AAABBB99C99Z999Z",
|
|
166
|
+
personalPhone: "+39-555-123-456",
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function createAgentGraph() {
|
|
171
|
+
const createCorporateIdentity = (0, index_1.tool)({
|
|
172
|
+
name: "create_corporate_identity",
|
|
173
|
+
description: "Create identity in the enterprise IAM.",
|
|
174
|
+
parameters: zod_1.z.object({}),
|
|
175
|
+
execute: (_input, runContext) => {
|
|
176
|
+
if (runContext) {
|
|
177
|
+
runContext.context.runtimeState.identityCreated = true;
|
|
178
|
+
}
|
|
179
|
+
return {
|
|
180
|
+
identityCreated: true,
|
|
181
|
+
};
|
|
182
|
+
},
|
|
183
|
+
});
|
|
184
|
+
const activateAccess = (0, index_1.tool)({
|
|
185
|
+
name: "activate_access",
|
|
186
|
+
description: "Activate application access for the employee.",
|
|
187
|
+
parameters: zod_1.z.object({
|
|
188
|
+
app: zod_1.z.string(),
|
|
189
|
+
privileged: zod_1.z.boolean().default(false),
|
|
190
|
+
}),
|
|
191
|
+
execute: (input, runContext) => {
|
|
192
|
+
if (runContext) {
|
|
193
|
+
runContext.context.runtimeState.accessActivated = true;
|
|
194
|
+
}
|
|
195
|
+
return {
|
|
196
|
+
activated: true,
|
|
197
|
+
app: input.app,
|
|
198
|
+
privileged: input.privileged,
|
|
199
|
+
};
|
|
200
|
+
},
|
|
201
|
+
});
|
|
202
|
+
const requestAppAccess = (0, index_1.tool)({
|
|
203
|
+
name: "request_app_access",
|
|
204
|
+
description: "Register a request for app access.",
|
|
205
|
+
parameters: zod_1.z.object({
|
|
206
|
+
app: zod_1.z.string(),
|
|
207
|
+
privileged: zod_1.z.boolean().default(false),
|
|
208
|
+
}),
|
|
209
|
+
execute: (input, runContext) => {
|
|
210
|
+
if (runContext) {
|
|
211
|
+
runContext.context.runtimeState.accessRequests.push(input.app);
|
|
212
|
+
}
|
|
213
|
+
return {
|
|
214
|
+
requested: true,
|
|
215
|
+
app: input.app,
|
|
216
|
+
privileged: input.privileged,
|
|
217
|
+
};
|
|
218
|
+
},
|
|
219
|
+
});
|
|
220
|
+
const securityAgent = new index_1.Agent({
|
|
221
|
+
name: "Security Agent",
|
|
222
|
+
model: "fake-model",
|
|
223
|
+
tools: [activateAccess],
|
|
224
|
+
});
|
|
225
|
+
const coordinatorAgent = new index_1.Agent({
|
|
226
|
+
name: "Onboarding Coordinator",
|
|
227
|
+
model: "fake-model",
|
|
228
|
+
tools: [createCorporateIdentity, activateAccess, requestAppAccess],
|
|
229
|
+
// Handoffs are exposed as synthetic tools and evaluated by handoffPolicy.
|
|
230
|
+
handoffs: [securityAgent],
|
|
231
|
+
});
|
|
232
|
+
return {
|
|
233
|
+
coordinatorAgent,
|
|
234
|
+
securityAgent,
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
function createRunOptions(context, records, scenario) {
|
|
238
|
+
return {
|
|
239
|
+
context,
|
|
240
|
+
policies: createPolicies(),
|
|
241
|
+
record: {
|
|
242
|
+
metadata: {
|
|
243
|
+
scenario,
|
|
244
|
+
},
|
|
245
|
+
contextRedactor: redactContext,
|
|
246
|
+
sink: (record) => {
|
|
247
|
+
records.push(record);
|
|
248
|
+
},
|
|
249
|
+
},
|
|
250
|
+
};
|
|
251
|
+
}
|
|
252
|
+
async function runHappyPathScenario() {
|
|
253
|
+
// Happy path: tool allow + handoff allow + privileged activation allow.
|
|
254
|
+
const records = [];
|
|
255
|
+
const context = createContext({
|
|
256
|
+
employeeId: "E-100",
|
|
257
|
+
requestedRole: "finance_admin",
|
|
258
|
+
actorDepartment: "HR",
|
|
259
|
+
actorGroups: ["onboarding-admin"],
|
|
260
|
+
mandatoryTrainingCompleted: true,
|
|
261
|
+
managerApproved: true,
|
|
262
|
+
securityApproved: true,
|
|
263
|
+
});
|
|
264
|
+
const { coordinatorAgent } = createAgentGraph();
|
|
265
|
+
(0, index_1.setDefaultProvider)(new SequencedProvider([
|
|
266
|
+
[
|
|
267
|
+
{
|
|
268
|
+
type: "tool_call",
|
|
269
|
+
callId: "call-1",
|
|
270
|
+
name: "create_corporate_identity",
|
|
271
|
+
arguments: "{}",
|
|
272
|
+
},
|
|
273
|
+
],
|
|
274
|
+
[
|
|
275
|
+
{
|
|
276
|
+
type: "tool_call",
|
|
277
|
+
callId: "call-2",
|
|
278
|
+
name: "handoff_to_security_agent",
|
|
279
|
+
arguments: JSON.stringify({
|
|
280
|
+
privileged: true,
|
|
281
|
+
app: "ERP_FINANCE",
|
|
282
|
+
}),
|
|
283
|
+
},
|
|
284
|
+
],
|
|
285
|
+
[
|
|
286
|
+
{
|
|
287
|
+
type: "tool_call",
|
|
288
|
+
callId: "call-3",
|
|
289
|
+
name: "activate_access",
|
|
290
|
+
arguments: JSON.stringify({
|
|
291
|
+
privileged: true,
|
|
292
|
+
app: "ERP_FINANCE",
|
|
293
|
+
}),
|
|
294
|
+
},
|
|
295
|
+
],
|
|
296
|
+
[
|
|
297
|
+
{
|
|
298
|
+
type: "completed",
|
|
299
|
+
message: "Onboarding completed: identity and privileged access are active.",
|
|
300
|
+
},
|
|
301
|
+
],
|
|
302
|
+
]));
|
|
303
|
+
const result = await (0, index_1.run)(coordinatorAgent, "Onboard employee E-100 with privileged ERP access.", createRunOptions(context, records, "happy_path"));
|
|
304
|
+
strict_1.default.equal(result.finalOutput, "Onboarding completed: identity and privileged access are active.");
|
|
305
|
+
strict_1.default.equal(result.lastAgent.name, "Security Agent");
|
|
306
|
+
strict_1.default.equal(context.runtimeState.identityCreated, true);
|
|
307
|
+
strict_1.default.equal(context.runtimeState.accessActivated, true);
|
|
308
|
+
strict_1.default.equal(records.length, 1);
|
|
309
|
+
strict_1.default.equal(records[0]?.status, "completed");
|
|
310
|
+
strict_1.default.equal(records[0]?.contextRedacted, true);
|
|
311
|
+
strict_1.default.equal(records[0]?.contextSnapshot.pii.taxId, "[REDACTED]");
|
|
312
|
+
strict_1.default.deepEqual(records[0]?.policyDecisions.map((decision) => decision.reason), [
|
|
313
|
+
"allow_onboarding_admin_identity_creation",
|
|
314
|
+
"allow_privileged_escalation_path",
|
|
315
|
+
"allow_access_activation_requirements_met",
|
|
316
|
+
]);
|
|
317
|
+
}
|
|
318
|
+
async function runComplianceDenyScenario() {
|
|
319
|
+
// Deny because compliance training is missing before activation.
|
|
320
|
+
const records = [];
|
|
321
|
+
const context = createContext({
|
|
322
|
+
employeeId: "E-101",
|
|
323
|
+
requestedRole: "standard_employee",
|
|
324
|
+
actorDepartment: "HR",
|
|
325
|
+
actorGroups: ["onboarding-admin"],
|
|
326
|
+
mandatoryTrainingCompleted: false,
|
|
327
|
+
managerApproved: true,
|
|
328
|
+
securityApproved: true,
|
|
329
|
+
});
|
|
330
|
+
const { coordinatorAgent } = createAgentGraph();
|
|
331
|
+
(0, index_1.setDefaultProvider)(new SequencedProvider([
|
|
332
|
+
[
|
|
333
|
+
{
|
|
334
|
+
type: "tool_call",
|
|
335
|
+
callId: "call-1",
|
|
336
|
+
name: "create_corporate_identity",
|
|
337
|
+
arguments: "{}",
|
|
338
|
+
},
|
|
339
|
+
],
|
|
340
|
+
[
|
|
341
|
+
{
|
|
342
|
+
type: "tool_call",
|
|
343
|
+
callId: "call-2",
|
|
344
|
+
name: "activate_access",
|
|
345
|
+
arguments: JSON.stringify({
|
|
346
|
+
privileged: false,
|
|
347
|
+
app: "INTRANET",
|
|
348
|
+
}),
|
|
349
|
+
},
|
|
350
|
+
],
|
|
351
|
+
]));
|
|
352
|
+
await strict_1.default.rejects(() => (0, index_1.run)(coordinatorAgent, "Onboard employee E-101 and activate intranet access.", createRunOptions(context, records, "deny_missing_training")), (error) => {
|
|
353
|
+
strict_1.default.ok(error instanceof index_1.ToolCallPolicyDeniedError);
|
|
354
|
+
strict_1.default.equal(error.result.policyResult.reason, "deny_missing_compliance_training");
|
|
355
|
+
return true;
|
|
356
|
+
});
|
|
357
|
+
strict_1.default.equal(context.runtimeState.identityCreated, true);
|
|
358
|
+
strict_1.default.equal(context.runtimeState.accessActivated, false);
|
|
359
|
+
strict_1.default.equal(records.length, 1);
|
|
360
|
+
strict_1.default.equal(records[0]?.status, "failed");
|
|
361
|
+
strict_1.default.equal(records[0]?.errorName, "ToolCallPolicyDeniedError");
|
|
362
|
+
strict_1.default.equal(records[0]?.policyDecisions.at(-1)?.reason, "deny_missing_compliance_training");
|
|
363
|
+
}
|
|
364
|
+
async function runApprovalDenyHandoffScenario() {
|
|
365
|
+
// Deny because privileged handoff requires manager approval.
|
|
366
|
+
const records = [];
|
|
367
|
+
const context = createContext({
|
|
368
|
+
employeeId: "E-102",
|
|
369
|
+
requestedRole: "payroll_admin",
|
|
370
|
+
actorDepartment: "HR",
|
|
371
|
+
actorGroups: ["onboarding-admin"],
|
|
372
|
+
mandatoryTrainingCompleted: true,
|
|
373
|
+
managerApproved: false,
|
|
374
|
+
securityApproved: true,
|
|
375
|
+
});
|
|
376
|
+
const { coordinatorAgent } = createAgentGraph();
|
|
377
|
+
(0, index_1.setDefaultProvider)(new SequencedProvider([
|
|
378
|
+
[
|
|
379
|
+
{
|
|
380
|
+
type: "tool_call",
|
|
381
|
+
callId: "call-1",
|
|
382
|
+
name: "handoff_to_security_agent",
|
|
383
|
+
arguments: JSON.stringify({
|
|
384
|
+
privileged: true,
|
|
385
|
+
app: "PAYROLL_ADMIN",
|
|
386
|
+
}),
|
|
387
|
+
},
|
|
388
|
+
],
|
|
389
|
+
]));
|
|
390
|
+
await strict_1.default.rejects(() => (0, index_1.run)(coordinatorAgent, "Onboard employee E-102 for payroll admin access.", createRunOptions(context, records, "deny_missing_manager_approval")), (error) => {
|
|
391
|
+
strict_1.default.ok(error instanceof index_1.HandoffPolicyDeniedError);
|
|
392
|
+
strict_1.default.equal(error.result.policyResult.reason, "deny_missing_manager_approval");
|
|
393
|
+
return true;
|
|
394
|
+
});
|
|
395
|
+
strict_1.default.equal(records.length, 1);
|
|
396
|
+
strict_1.default.equal(records[0]?.status, "failed");
|
|
397
|
+
strict_1.default.equal(records[0]?.errorName, "HandoffPolicyDeniedError");
|
|
398
|
+
strict_1.default.equal(records[0]?.policyDecisions.length, 1);
|
|
399
|
+
strict_1.default.equal(records[0]?.policyDecisions[0]?.resource.kind, "handoff");
|
|
400
|
+
strict_1.default.equal(records[0]?.policyDecisions[0]?.reason, "deny_missing_manager_approval");
|
|
401
|
+
}
|
|
402
|
+
async function runAccessDriftScenario() {
|
|
403
|
+
// Same action across t0/t1: authorization drifts when group membership changes.
|
|
404
|
+
const allowRecords = [];
|
|
405
|
+
const denyRecords = [];
|
|
406
|
+
const { coordinatorAgent } = createAgentGraph();
|
|
407
|
+
(0, index_1.setDefaultProvider)(new SequencedProvider([
|
|
408
|
+
[
|
|
409
|
+
{
|
|
410
|
+
type: "tool_call",
|
|
411
|
+
callId: "call-1",
|
|
412
|
+
name: "request_app_access",
|
|
413
|
+
arguments: JSON.stringify({
|
|
414
|
+
app: "CRM_SALES",
|
|
415
|
+
privileged: false,
|
|
416
|
+
}),
|
|
417
|
+
},
|
|
418
|
+
],
|
|
419
|
+
[{ type: "completed", message: "Access request registered." }],
|
|
420
|
+
]));
|
|
421
|
+
const contextAtT0 = createContext({
|
|
422
|
+
employeeId: "E-103",
|
|
423
|
+
requestedRole: "sales_rep",
|
|
424
|
+
actorDepartment: "Sales",
|
|
425
|
+
actorGroups: ["sales"],
|
|
426
|
+
mandatoryTrainingCompleted: true,
|
|
427
|
+
managerApproved: true,
|
|
428
|
+
securityApproved: true,
|
|
429
|
+
});
|
|
430
|
+
const resultAtT0 = await (0, index_1.run)(coordinatorAgent, "Request CRM access for employee E-103.", createRunOptions(contextAtT0, allowRecords, "drift_t0_allow"));
|
|
431
|
+
strict_1.default.equal(resultAtT0.finalOutput, "Access request registered.");
|
|
432
|
+
strict_1.default.equal(contextAtT0.runtimeState.accessRequests.length, 1);
|
|
433
|
+
strict_1.default.equal(allowRecords[0]?.policyDecisions[0]?.reason, "allow_department_scope_match");
|
|
434
|
+
(0, index_1.setDefaultProvider)(new SequencedProvider([
|
|
435
|
+
[
|
|
436
|
+
{
|
|
437
|
+
type: "tool_call",
|
|
438
|
+
callId: "call-1",
|
|
439
|
+
name: "request_app_access",
|
|
440
|
+
arguments: JSON.stringify({
|
|
441
|
+
app: "CRM_SALES",
|
|
442
|
+
privileged: false,
|
|
443
|
+
}),
|
|
444
|
+
},
|
|
445
|
+
],
|
|
446
|
+
[{ type: "completed", message: "Access request registered." }],
|
|
447
|
+
]));
|
|
448
|
+
const contextAtT1 = createContext({
|
|
449
|
+
employeeId: "E-103",
|
|
450
|
+
requestedRole: "sales_rep",
|
|
451
|
+
actorDepartment: "Sales",
|
|
452
|
+
actorGroups: [],
|
|
453
|
+
mandatoryTrainingCompleted: true,
|
|
454
|
+
managerApproved: true,
|
|
455
|
+
securityApproved: true,
|
|
456
|
+
});
|
|
457
|
+
await strict_1.default.rejects(() => (0, index_1.run)(coordinatorAgent, "Request CRM access for employee E-103.", createRunOptions(contextAtT1, denyRecords, "drift_t1_deny")), (error) => {
|
|
458
|
+
strict_1.default.ok(error instanceof index_1.ToolCallPolicyDeniedError);
|
|
459
|
+
strict_1.default.equal(error.result.policyResult.reason, "deny_actor_group_mismatch");
|
|
460
|
+
return true;
|
|
461
|
+
});
|
|
462
|
+
strict_1.default.equal(contextAtT1.runtimeState.accessRequests.length, 0);
|
|
463
|
+
strict_1.default.equal(denyRecords[0]?.policyDecisions[0]?.reason, "deny_actor_group_mismatch");
|
|
464
|
+
strict_1.default.equal(denyRecords[0]?.status, "failed");
|
|
465
|
+
}
|
|
466
|
+
async function main() {
|
|
467
|
+
await runHappyPathScenario();
|
|
468
|
+
await runComplianceDenyScenario();
|
|
469
|
+
await runApprovalDenyHandoffScenario();
|
|
470
|
+
await runAccessDriftScenario();
|
|
471
|
+
process.stdout.write("Onboarding governance smoke passed.\n");
|
|
472
|
+
}
|
|
473
|
+
main().catch((error) => {
|
|
474
|
+
const message = error instanceof Error ? `${error.name}: ${error.message}` : String(error);
|
|
475
|
+
process.stderr.write(`${message}\n`);
|
|
476
|
+
process.exit(1);
|
|
477
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"onboarding-governance-tutorial.d.ts","sourceRoot":"","sources":["../../src/examples/onboarding-governance-tutorial.ts"],"names":[],"mappings":""}
|