agentlang 0.1.6 → 0.1.7

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "agentlang",
3
3
  "description": "The easiest way to build the most reliable AI agents - enterprise-grade teams of AI agents that collaborate with each other and humans",
4
- "version": "0.1.6",
4
+ "version": "0.1.7",
5
5
  "license": "Sustainable Use License",
6
6
  "author": "agentlang-ai",
7
7
  "homepage": "https://github.com/agentlang-ai/agentlang#readme",
@@ -1,5 +1,7 @@
1
1
  import {
2
2
  AgentlangAuth,
3
+ InviteUserCallback,
4
+ InvitationInfo,
3
5
  LoginCallback,
4
6
  LogoutCallback,
5
7
  SessionInfo,
@@ -40,6 +42,8 @@ let ForgotPasswordCommand: any = undefined;
40
42
  let ConfirmForgotPasswordCommand: any = undefined;
41
43
  let AdminGetUserCommand: any = undefined;
42
44
  let InitiateAuthCommand: any = undefined;
45
+ let AdminCreateUserCommand: any = undefined;
46
+ let RespondToAuthChallengeCommand: any = undefined;
43
47
  let AuthenticationDetails: any = undefined;
44
48
  let CognitoUser: any = undefined;
45
49
  let CognitoUserPool: any = undefined;
@@ -61,6 +65,8 @@ if (isNodeEnv) {
61
65
  ConfirmForgotPasswordCommand = cip.ConfirmForgotPasswordCommand;
62
66
  AdminGetUserCommand = cip.AdminGetUserCommand;
63
67
  InitiateAuthCommand = cip.InitiateAuthCommand;
68
+ AdminCreateUserCommand = cip.AdminCreateUserCommand;
69
+ RespondToAuthChallengeCommand = cip.RespondToAuthChallengeCommand;
64
70
 
65
71
  const ci = await import('amazon-cognito-identity-js');
66
72
  AuthenticationDetails = ci.AuthenticationDetails;
@@ -1013,4 +1019,150 @@ export class CognitoAuth implements AgentlangAuth {
1013
1019
  throw err; // This line won't be reached due to handleCognitoError throwing
1014
1020
  }
1015
1021
  }
1022
+
1023
+ async inviteUser(
1024
+ email: string,
1025
+ firstName: string,
1026
+ lastName: string,
1027
+ userData: Map<string, any> | undefined,
1028
+ env: Environment,
1029
+ cb: InviteUserCallback
1030
+ ): Promise<void> {
1031
+ try {
1032
+ const client = new CognitoIdentityProviderClient({
1033
+ region: process.env.AWS_REGION || 'us-west-2',
1034
+ });
1035
+
1036
+ const userAttrs = [
1037
+ {
1038
+ Name: 'email',
1039
+ Value: email,
1040
+ },
1041
+ {
1042
+ Name: 'email_verified',
1043
+ Value: 'true',
1044
+ },
1045
+ {
1046
+ Name: 'given_name',
1047
+ Value: firstName,
1048
+ },
1049
+ {
1050
+ Name: 'family_name',
1051
+ Value: lastName,
1052
+ },
1053
+ ];
1054
+
1055
+ if (userData) {
1056
+ userData.forEach((v: any, k: string) => {
1057
+ userAttrs.push({ Name: k, Value: String(v) });
1058
+ });
1059
+ }
1060
+
1061
+ let userExists = false;
1062
+ try {
1063
+ const getUserCommand = new AdminGetUserCommand({
1064
+ UserPoolId: this.fetchUserPoolId(),
1065
+ Username: email,
1066
+ });
1067
+ await client.send(getUserCommand);
1068
+ userExists = true;
1069
+ logger.debug(`User ${email} already exists, will resend invitation`);
1070
+ } catch (err: any) {
1071
+ if (err.name !== 'UserNotFoundException') {
1072
+ throw err;
1073
+ }
1074
+ logger.debug(`User ${email} does not exist, will create new user`);
1075
+ }
1076
+
1077
+ const command = new AdminCreateUserCommand({
1078
+ UserPoolId: this.fetchUserPoolId(),
1079
+ Username: email,
1080
+ UserAttributes: userAttrs,
1081
+ DesiredDeliveryMediums: ['EMAIL'],
1082
+ ...(userExists ? { MessageAction: 'RESEND' } : {}),
1083
+ });
1084
+
1085
+ logger.debug(`Attempting to invite user: ${email}`);
1086
+ const response = await client.send(command);
1087
+
1088
+ if (response.$metadata.httpStatusCode === 200) {
1089
+ logger.info(`User invitation successful for: ${email}`);
1090
+
1091
+ await ensureUser(email, firstName, lastName, env);
1092
+
1093
+ const invitationInfo: InvitationInfo = {
1094
+ email: email,
1095
+ firstName: firstName,
1096
+ lastName: lastName,
1097
+ invitationId: response.User?.Username,
1098
+ systemInvitationInfo: response,
1099
+ };
1100
+
1101
+ cb(invitationInfo);
1102
+ } else {
1103
+ logger.error(
1104
+ `User invitation failed with HTTP status ${response.$metadata.httpStatusCode}`,
1105
+ {
1106
+ email: email,
1107
+ statusCode: response.$metadata.httpStatusCode,
1108
+ }
1109
+ );
1110
+ throw new BadRequestError(
1111
+ `User invitation failed with status ${response.$metadata.httpStatusCode}`
1112
+ );
1113
+ }
1114
+ } catch (err: any) {
1115
+ if (err instanceof BadRequestError) throw err;
1116
+ logger.error(`User invitation error for ${email}:`, {
1117
+ errorName: err.name,
1118
+ errorMessage: sanitizeErrorMessage(err.message),
1119
+ });
1120
+ handleCognitoError(err, 'inviteUser');
1121
+ }
1122
+ }
1123
+
1124
+ async acceptInvitation(
1125
+ email: string,
1126
+ tempPassword: string,
1127
+ newPassword: string,
1128
+ _env: Environment
1129
+ ): Promise<void> {
1130
+ try {
1131
+ const client = new CognitoIdentityProviderClient({
1132
+ region: process.env.AWS_REGION || 'us-west-2',
1133
+ credentials: fromEnv(),
1134
+ });
1135
+
1136
+ const initAuth = new InitiateAuthCommand({
1137
+ AuthFlow: 'USER_PASSWORD_AUTH',
1138
+ ClientId: this.fetchClientId(),
1139
+ AuthParameters: {
1140
+ USERNAME: email,
1141
+ PASSWORD: tempPassword,
1142
+ },
1143
+ });
1144
+
1145
+ const initResponse = await client.send(initAuth);
1146
+
1147
+ if (initResponse.ChallengeName === 'NEW_PASSWORD_REQUIRED') {
1148
+ const respond = new RespondToAuthChallengeCommand({
1149
+ ClientId: this.fetchClientId(),
1150
+ ChallengeName: 'NEW_PASSWORD_REQUIRED',
1151
+ Session: initResponse.Session,
1152
+ ChallengeResponses: {
1153
+ USERNAME: email,
1154
+ NEW_PASSWORD: newPassword,
1155
+ },
1156
+ });
1157
+
1158
+ await client.send(respond);
1159
+ logger.info(`User invitation accepted successfully for: ${email}`);
1160
+ } else {
1161
+ throw new Error(`Unexpected challenge: ${initResponse.ChallengeName}`);
1162
+ }
1163
+ } catch (err: any) {
1164
+ logger.error(`Accept invitation failed for ${email}: ${sanitizeErrorMessage(err.message)}`);
1165
+ handleCognitoError(err, 'acceptInvitation');
1166
+ }
1167
+ }
1016
1168
  }
@@ -21,6 +21,16 @@ export type SessionInfo = {
21
21
  export type SignUpCallback = (userInfo: UserInfo) => void;
22
22
  export type LoginCallback = (sessionInfo: SessionInfo) => void;
23
23
  export type LogoutCallback = (status: boolean) => void;
24
+ export type InviteUserCallback = (invitationInfo: InvitationInfo) => void;
25
+
26
+ export type InvitationInfo = {
27
+ email: string;
28
+ firstName: string;
29
+ lastName: string;
30
+ tempPassword?: string;
31
+ invitationId?: string;
32
+ systemInvitationInfo?: any;
33
+ };
24
34
 
25
35
  export interface AgentlangAuth {
26
36
  signUp(
@@ -53,4 +63,18 @@ export interface AgentlangAuth {
53
63
  env: Environment
54
64
  ): Promise<boolean>;
55
65
  refreshToken(refreshToken: string, env: Environment): Promise<SessionInfo>;
66
+ inviteUser(
67
+ email: string,
68
+ firstName: string,
69
+ lastName: string,
70
+ userData: Map<string, any> | undefined,
71
+ env: Environment,
72
+ cb: InviteUserCallback
73
+ ): Promise<void>;
74
+ acceptInvitation(
75
+ email: string,
76
+ tempPassword: string,
77
+ newPassword: string,
78
+ env: Environment
79
+ ): Promise<void>;
56
80
  }
@@ -218,12 +218,18 @@ export async function executeGraph(execGraph: ExecGraph, env: Environment): Prom
218
218
  case SubGraphType.EVENT:
219
219
  await evaluateStatement(node.code as Statement, env);
220
220
  break;
221
- case SubGraphType.IF:
222
- await executeIfSubGraph(subg, env);
221
+ case SubGraphType.IF: {
222
+ const newEnv = new Environment(`${env.name}-if`, env);
223
+ await executeIfSubGraph(subg, newEnv);
224
+ env.setLastResult(newEnv.getLastResult());
223
225
  break;
224
- case SubGraphType.FOR_EACH:
225
- await executeForEachSubGraph(subg, node, env);
226
+ }
227
+ case SubGraphType.FOR_EACH: {
228
+ const newEnv = new Environment(`${env.name}-forEach`, env);
229
+ await executeForEachSubGraph(subg, node, newEnv);
230
+ env.setLastResult(newEnv.getLastResult());
226
231
  break;
232
+ }
227
233
  case SubGraphType.DELETE:
228
234
  await executeDeleteSubGraph(subg, node, env);
229
235
  break;
@@ -140,6 +140,7 @@ export class Environment extends Instance {
140
140
  private activeCatchHandlers: Array<CatchHandlers>;
141
141
  private eventExecutor: Function | undefined = undefined;
142
142
  private statementsExecutor: Function | undefined = undefined;
143
+ private scratchPad: any = undefined;
143
144
 
144
145
  private activeUserData: any = undefined;
145
146
 
@@ -270,6 +271,28 @@ export class Environment extends Instance {
270
271
  return this.attributes.get(Environment.FlowContextTag);
271
272
  }
272
273
 
274
+ addToScratchPad(k: string, data: any): Environment {
275
+ if (this.scratchPad == undefined) {
276
+ this.scratchPad = {};
277
+ }
278
+ this.scratchPad[k] = data;
279
+ return this;
280
+ }
281
+
282
+ getScratchPad(): any {
283
+ return this.scratchPad;
284
+ }
285
+
286
+ resetScratchPad(): Environment {
287
+ this.scratchPad = undefined;
288
+ return this;
289
+ }
290
+
291
+ setScratchPad(obj: any): Environment {
292
+ this.scratchPad = obj;
293
+ return this;
294
+ }
295
+
273
296
  static SuspensionUserData = '^';
274
297
 
275
298
  bindSuspensionUserData(userData: string): Environment {
@@ -1365,10 +1388,12 @@ async function evaluateCrudMap(crud: CrudMap, env: Environment): Promise<void> {
1365
1388
  else if (isDocEventInstance(inst)) await handleDocEvent(inst, env);
1366
1389
  else {
1367
1390
  const eventExec = env.getEventExecutor();
1391
+ const newEnv = new Environment(`${inst.name}.env`, env);
1368
1392
  if (eventExec) {
1369
- await eventExec(inst, env);
1393
+ await eventExec(inst, newEnv);
1394
+ env.setLastResult(newEnv.getLastResult());
1370
1395
  } else {
1371
- await evaluate(inst, (result: Result) => env.setLastResult(result), env);
1396
+ await evaluate(inst, (result: Result) => env.setLastResult(result), newEnv);
1372
1397
  }
1373
1398
  env.resetReturnFlag();
1374
1399
  }
@@ -1480,31 +1505,7 @@ async function evaluateJoinQuery(
1480
1505
  }
1481
1506
  const resolver = await getResolverForPath(inst.name, moduleName, env);
1482
1507
  const result: Result = await resolver.queryByJoin(inst, joinsSpec, normIntoSpec, distinct);
1483
-
1484
- const transformedResult = transformDateFieldsInJoinResult(result);
1485
-
1486
- env.setLastResult(transformedResult);
1487
- }
1488
-
1489
- function transformDateFieldsInJoinResult(result: any): any {
1490
- if (!result || !Array.isArray(result)) {
1491
- return result;
1492
- }
1493
- return result.map((row: any) => {
1494
- if (typeof row !== 'object' || row === null) {
1495
- return row;
1496
- }
1497
-
1498
- for (const [key, value] of Object.entries(row)) {
1499
- if (value && value instanceof Date) {
1500
- if (value instanceof Date) {
1501
- row[key] = value.toLocaleDateString('en-CA');
1502
- }
1503
- }
1504
- }
1505
-
1506
- return row;
1507
- });
1508
+ env.setLastResult(result);
1508
1509
  }
1509
1510
 
1510
1511
  async function walkJoinQueryPattern(
@@ -1539,6 +1540,11 @@ async function agentInvoke(agent: AgentInstance, msg: string, env: Environment):
1539
1540
  const flowContext = env.getFlowContext();
1540
1541
  msg = flowContext ? `context: ${flowContext}\n${msg}` : msg;
1541
1542
 
1543
+ const scratchPad = env.getScratchPad();
1544
+ if (scratchPad) {
1545
+ const spad = JSON.stringify(scratchPad);
1546
+ msg = `${msg}\nScratchpad:\n${spad}`;
1547
+ }
1542
1548
  // log invocation details
1543
1549
  let invokeDebugMsg = `\nInvoking agent ${agent.name}:`;
1544
1550
  if (agent.role) {
@@ -1589,6 +1595,7 @@ async function agentInvoke(agent: AgentInstance, msg: string, env: Environment):
1589
1595
  env.setLastResult(await parseAndEvaluateStatement(rs, undefined, env));
1590
1596
  }
1591
1597
  }
1598
+ agent.maybeAddScratchData(env);
1592
1599
  break;
1593
1600
  } catch (err: any) {
1594
1601
  if (retries < MAX_PLANNER_RETRIES) {
@@ -1604,6 +1611,30 @@ async function agentInvoke(agent: AgentInstance, msg: string, env: Environment):
1604
1611
  }
1605
1612
  }
1606
1613
  }
1614
+ } else {
1615
+ let retries = 0;
1616
+ while (true) {
1617
+ try {
1618
+ const obj = agent.maybeValidateJsonResponse(result);
1619
+ if (obj != undefined) {
1620
+ env.setLastResult(obj);
1621
+ env.addToScratchPad(agent.getFqName(), obj);
1622
+ }
1623
+ break;
1624
+ } catch (err: any) {
1625
+ if (retries < MAX_PLANNER_RETRIES) {
1626
+ await agent.invoke(`Please fix these errors:\n ${err}`, env);
1627
+ const r: string | undefined = env.getLastResult();
1628
+ result = r;
1629
+ ++retries;
1630
+ } else {
1631
+ logger.error(
1632
+ `Failed to validate JSON response generated by agent ${agent.name} - ${result}, ${err}`
1633
+ );
1634
+ break;
1635
+ }
1636
+ }
1637
+ }
1607
1638
  }
1608
1639
  } else {
1609
1640
  throw new Error(`Agent ${agent.name} failed to generate a response`);
@@ -1635,6 +1666,7 @@ async function handleAgentInvocationWithFlow(
1635
1666
  ): Promise<void> {
1636
1667
  rootAgent.markAsFlowExecutor();
1637
1668
  await iterateOnFlow(flow, rootAgent, msg, env);
1669
+ env.resetScratchPad();
1638
1670
  }
1639
1671
 
1640
1672
  async function saveFlowSuspension(
@@ -1643,9 +1675,10 @@ async function saveFlowSuspension(
1643
1675
  step: FlowStep,
1644
1676
  env: Environment
1645
1677
  ): Promise<void> {
1678
+ const spad = env.getScratchPad() || {};
1646
1679
  const suspId = await createSuspension(
1647
1680
  env.getSuspensionId(),
1648
- [FlowSuspensionTag, agent.name, step, context],
1681
+ [FlowSuspensionTag, agent.name, step, context, JSON.stringify(spad)],
1649
1682
  env
1650
1683
  );
1651
1684
  env.setLastResult({ suspension: suspId || 'null' });
@@ -1656,11 +1689,12 @@ export async function restartFlow(
1656
1689
  userData: string,
1657
1690
  env: Environment
1658
1691
  ): Promise<void> {
1659
- const [_, agentName, step, ctx] = flowContext;
1692
+ const [_, agentName, step, ctx, spad] = flowContext;
1660
1693
  const rootAgent: AgentInstance = await findAgentByName(agentName, env);
1661
1694
  const flow = getAgentFlow(agentName, rootAgent.moduleName);
1662
1695
  if (flow) {
1663
1696
  const newCtx = `${ctx}\n${step} --> ${userData}\n`;
1697
+ env.setScratchPad(JSON.parse(spad));
1664
1698
  await iterateOnFlow(flow, rootAgent, newCtx, env);
1665
1699
  }
1666
1700
  }
@@ -1674,7 +1708,7 @@ async function iterateOnFlow(
1674
1708
  env: Environment
1675
1709
  ): Promise<void> {
1676
1710
  rootAgent.disableSession();
1677
- const s = `Now consider the following flowchart and context:\n${flow}\n\n${msg}
1711
+ const s = `Now consider the following flowchart and context:\n${flow}\n\nInitial context: ${msg}\n\n
1678
1712
  If you understand from the context that a step with no further possible steps has been evaluated,
1679
1713
  terminate the flowchart by returning DONE. Never return to the top or root step of the flowchart, instead return DONE.\n`;
1680
1714
  await agentInvoke(rootAgent, s, env);
@@ -1710,7 +1744,7 @@ async function iterateOnFlow(
1710
1744
  console.debug(
1711
1745
  `\n----> Completed execution of step ${step}, iteration id ${iterId} with result:\n${rs}`
1712
1746
  );
1713
- context = `${context}\nExecuted steps: ${[...executedSteps].join(', ')}\n${step} --> ${rs}\n`;
1747
+ context = `${context}\nExecuted steps: ${[...executedSteps].join(', ')}`;
1714
1748
  await agentInvoke(rootAgent, `${s}\n${context}`, env);
1715
1749
  step = env.getLastResult().trim();
1716
1750
  }
@@ -78,7 +78,9 @@ import {
78
78
  LlmEntityName,
79
79
  registerAgentDirectives,
80
80
  registerAgentGlossary,
81
+ registerAgentResponseSchema,
81
82
  registerAgentScenarios,
83
+ registerAgentScratchNames,
82
84
  } from './modules/ai.js';
83
85
  import { getDefaultLLMService } from './agents/registry.js';
84
86
  import { GenericResolver, GenericResolverMethods } from './resolvers/interface.js';
@@ -566,6 +568,20 @@ function processAgentGlossary(agentName: string, value: Literal): AgentGlossaryE
566
568
  return undefined;
567
569
  }
568
570
 
571
+ function processAgentScratchNames(agentName: string, value: Literal): string[] | undefined {
572
+ if (value.array) {
573
+ const scratch = new Array<string>();
574
+ value.array.vals.forEach((stmt: Statement) => {
575
+ const expr = stmt.pattern.expr;
576
+ if (expr && isLiteral(expr) && (expr.id || expr.str)) {
577
+ scratch.push(expr.id || expr.str || '');
578
+ }
579
+ });
580
+ return scratch;
581
+ }
582
+ return undefined;
583
+ }
584
+
569
585
  async function addAgentDefinition(def: AgentDefinition, moduleName: string) {
570
586
  let llmName: string | undefined = undefined;
571
587
  const name = def.name;
@@ -577,6 +593,8 @@ async function addAgentDefinition(def: AgentDefinition, moduleName: string) {
577
593
  let conds: AgentCondition[] | undefined = undefined;
578
594
  let scenarios: AgentScenario[] | undefined = undefined;
579
595
  let glossary: AgentGlossaryEntry[] | undefined = undefined;
596
+ let responseSchema: string | undefined = undefined;
597
+ let scratchNames: string[] | undefined = undefined;
580
598
  def.body?.attributes.forEach((apdef: GenericPropertyDef) => {
581
599
  if (apdef.name == 'flows') {
582
600
  let fnames: string | undefined = undefined;
@@ -604,6 +622,19 @@ async function addAgentDefinition(def: AgentDefinition, moduleName: string) {
604
622
  scenarios = processAgentScenarios(name, apdef.value);
605
623
  } else if (apdef.name == 'glossary') {
606
624
  glossary = processAgentGlossary(name, apdef.value);
625
+ } else if (apdef.name == 'responseSchema') {
626
+ const s = apdef.value.id || apdef.value.str || apdef.value.id;
627
+ if (s) {
628
+ if (isFqName(s)) {
629
+ responseSchema = s;
630
+ } else {
631
+ responseSchema = makeFqName(moduleName, s);
632
+ }
633
+ } else {
634
+ throw new Error(`responseSchema must be a valid name in agent ${name}`);
635
+ }
636
+ } else if (apdef.name == 'scratch') {
637
+ scratchNames = processAgentScratchNames(name, apdef.value);
607
638
  } else {
608
639
  let v: any = undefined;
609
640
  if (apdef.value.array) {
@@ -666,6 +697,12 @@ async function addAgentDefinition(def: AgentDefinition, moduleName: string) {
666
697
  if (glossary) {
667
698
  registerAgentGlossary(moduleName, name, glossary);
668
699
  }
700
+ if (responseSchema) {
701
+ registerAgentResponseSchema(moduleName, name, responseSchema);
702
+ }
703
+ if (scratchNames) {
704
+ registerAgentScratchNames(moduleName, name, scratchNames);
705
+ }
669
706
  // Don't add llm to module attrs if it wasn't originally specified
670
707
  addAgent(def.name, attrs, moduleName);
671
708
  }
@@ -3139,3 +3139,23 @@ export function getEntityRbacRules(entityFqName: string): RbacSpecification[] |
3139
3139
  }
3140
3140
  return undefined;
3141
3141
  }
3142
+
3143
+ export function asJSONSchema(fqName: string): string {
3144
+ const parts = splitFqName(fqName);
3145
+ if (parts.hasModule()) {
3146
+ const mod = fetchModule(parts.getModuleName());
3147
+ const record = mod.getRecord(parts.getEntryName());
3148
+ const s = new Array<string>();
3149
+ record.schema.forEach((attr: AttributeSpec, n: string) => {
3150
+ let t = attr.type;
3151
+ const enums = getEnumValues(attr);
3152
+ if (enums) {
3153
+ t = `one of the strings: ${[...enums].join(',')}`;
3154
+ }
3155
+ s.push(`"${n}": <${t}>`);
3156
+ });
3157
+ return `{${s.join(',\n')}}`;
3158
+ } else {
3159
+ throw new Error(`Failed to find module for ${fqName}`);
3160
+ }
3161
+ }
@@ -6,6 +6,7 @@ import {
6
6
  parseAndEvaluateStatement,
7
7
  } from '../interpreter.js';
8
8
  import {
9
+ asJSONSchema,
9
10
  fetchModule,
10
11
  Instance,
11
12
  instanceToObject,
@@ -129,6 +130,26 @@ export function registerAgentGlossary(
129
130
  AgentGlossary.set(makeFqName(moduleName, agentName), glossary);
130
131
  }
131
132
 
133
+ const AgentResponseSchema = new Map<string, string>();
134
+
135
+ export function registerAgentResponseSchema(
136
+ moduleName: string,
137
+ agentName: string,
138
+ responseSchema: string
139
+ ) {
140
+ AgentResponseSchema.set(makeFqName(moduleName, agentName), responseSchema);
141
+ }
142
+
143
+ const AgentScratchNames = new Map<string, Set<string>>();
144
+
145
+ export function registerAgentScratchNames(
146
+ moduleName: string,
147
+ agentName: string,
148
+ scratch: string[]
149
+ ) {
150
+ AgentScratchNames.set(makeFqName(moduleName, agentName), new Set(scratch));
151
+ }
152
+
132
153
  export class AgentInstance {
133
154
  llm: string = '';
134
155
  name: string = '';
@@ -145,6 +166,7 @@ export class AgentInstance {
145
166
  private toolsArray: string[] | undefined = undefined;
146
167
  private hasModuleTools = false;
147
168
  private withSession = true;
169
+ private fqName: string | undefined;
148
170
 
149
171
  private constructor() {}
150
172
 
@@ -241,7 +263,7 @@ export class AgentInstance {
241
263
  if (this.cachedInstruction) {
242
264
  return this.cachedInstruction;
243
265
  }
244
- const fqName = makeFqName(this.moduleName, this.name);
266
+ const fqName = this.getFqName();
245
267
  this.cachedInstruction = `${this.instruction || ''} ${this.directivesAsString(fqName)}`;
246
268
  const gls = AgentGlossary.get(fqName);
247
269
  if (gls) {
@@ -262,14 +284,80 @@ export class AgentInstance {
262
284
  });
263
285
  this.cachedInstruction = `${this.cachedInstruction}\nHere are some example user requests and the corresponding responses you are supposed to produce:\n${scs.join('\n')}`;
264
286
  }
287
+ const responseSchema = AgentResponseSchema.get(fqName);
288
+ if (responseSchema) {
289
+ this.cachedInstruction = `${this.cachedInstruction}\nReturn your response in the following JSON schema:\n${asJSONSchema(responseSchema)}
290
+ Only return a pure JSON object with no extra text, annotations etc.`;
291
+ }
265
292
  return this.cachedInstruction;
266
293
  }
267
294
 
295
+ maybeValidateJsonResponse(response: string | undefined): object | undefined {
296
+ if (response) {
297
+ const responseSchema = AgentResponseSchema.get(this.getFqName());
298
+ if (responseSchema) {
299
+ const attrs = JSON.parse(response);
300
+ const parts = splitFqName(responseSchema);
301
+ const moduleName = parts.getModuleName();
302
+ const entryName = parts.getEntryName();
303
+ const attrsMap = new Map(Object.entries(attrs));
304
+ const scm = fetchModule(moduleName).getRecord(entryName).schema;
305
+ const recAttrs = new Map<string, any>();
306
+ attrsMap.forEach((v: any, k: string) => {
307
+ if (scm.has(k)) {
308
+ recAttrs.set(k, v);
309
+ }
310
+ });
311
+ makeInstance(moduleName, entryName, recAttrs);
312
+ return attrs;
313
+ }
314
+ }
315
+ return undefined;
316
+ }
317
+
318
+ getFqName(): string {
319
+ if (this.fqName == undefined) {
320
+ this.fqName = makeFqName(this.moduleName, this.name);
321
+ }
322
+ return this.fqName;
323
+ }
324
+
268
325
  markAsFlowExecutor(): AgentInstance {
269
326
  this.type = 'flow-exec';
270
327
  return this;
271
328
  }
272
329
 
330
+ getScratchNames(): Set<string> | undefined {
331
+ return AgentScratchNames.get(this.getFqName());
332
+ }
333
+
334
+ maybeAddScratchData(env: Environment): AgentInstance {
335
+ const obj: any = env.getLastResult();
336
+ let r: Instance | Instance[] | undefined = undefined;
337
+ if (
338
+ obj instanceof Instance ||
339
+ (obj instanceof Array && obj.length > 0 && obj[0] instanceof Instance)
340
+ ) {
341
+ r = obj;
342
+ } else {
343
+ return this;
344
+ }
345
+ const scratchNames = this.getScratchNames();
346
+ let data: any = undefined;
347
+ let n = '';
348
+ if (r instanceof Array) {
349
+ data = r.map((inst: Instance) => {
350
+ return extractScratchData(scratchNames, inst);
351
+ });
352
+ n = r[0].getFqName();
353
+ } else {
354
+ data = extractScratchData(scratchNames, r);
355
+ n = r.getFqName();
356
+ }
357
+ if (data) env.addToScratchPad(n, data);
358
+ return this;
359
+ }
360
+
273
361
  async invoke(message: string, env: Environment) {
274
362
  const p = await findProviderForLLM(this.llm, env);
275
363
  const agentName = this.name;
@@ -411,6 +499,20 @@ export class AgentInstance {
411
499
  }
412
500
  }
413
501
 
502
+ function extractScratchData(scratchNames: Set<string> | undefined, inst: Instance): any {
503
+ const data: any = {};
504
+ inst.attributes.forEach((v: any, k: string) => {
505
+ if (scratchNames) {
506
+ if (scratchNames.has(k)) {
507
+ data[k] = v;
508
+ }
509
+ } else {
510
+ data[k] = v;
511
+ }
512
+ });
513
+ return data;
514
+ }
515
+
414
516
  async function parseHelper(stmt: string, env: Environment): Promise<any> {
415
517
  await parseAndEvaluateStatement(stmt, undefined, env);
416
518
  return env.getLastResult();
@@ -332,6 +332,15 @@ workflow getUser {
332
332
  workflow getUserByEmail {
333
333
  await Auth.getUserInfoByEmail(getUserByEmail.email)
334
334
  }
335
+
336
+ workflow inviteUser {
337
+ await Auth.inviteUser(inviteUser.email, inviteUser.firstName, inviteUser.lastName, inviteUser.userData)
338
+ }
339
+
340
+
341
+ workflow acceptInvitation {
342
+ await Auth.acceptInvitationUser(acceptInvitation.email, acceptInvitation.tempPassword, acceptInvitation.newPassword)
343
+ }
335
344
  `;
336
345
 
337
346
  const evalEvent = makeEventEvaluator(CoreAuthModuleName);
@@ -1113,6 +1122,68 @@ export async function refreshUserToken(refreshToken: string, env: Environment):
1113
1122
  }
1114
1123
  }
1115
1124
 
1125
+ export async function inviteUser(
1126
+ email: string,
1127
+ firstName: string,
1128
+ lastName: string,
1129
+ userData: Map<string, any> | undefined,
1130
+ env: Environment
1131
+ ): Promise<object> {
1132
+ const needCommit = env ? false : true;
1133
+ env = env ? env : new Environment();
1134
+ const f = async () => {
1135
+ try {
1136
+ let invitationInfo: any;
1137
+ await fetchAuthImpl().inviteUser(email, firstName, lastName, userData, env, (info: any) => {
1138
+ invitationInfo = info;
1139
+ });
1140
+
1141
+ return {
1142
+ email: invitationInfo.email,
1143
+ firstName: invitationInfo.firstName,
1144
+ lastName: invitationInfo.lastName,
1145
+ invitationId: invitationInfo.invitationId,
1146
+ message: 'User invitation sent successfully',
1147
+ };
1148
+ } catch (err: any) {
1149
+ logger.error(`User invitation failed: ${err.message}`);
1150
+ throw err;
1151
+ }
1152
+ };
1153
+ if (needCommit) {
1154
+ return await env.callInTransaction(f);
1155
+ } else {
1156
+ return await f();
1157
+ }
1158
+ }
1159
+
1160
+ export async function acceptInvitationUser(
1161
+ email: string,
1162
+ tempPassword: string,
1163
+ newPassword: string,
1164
+ env: Environment
1165
+ ): Promise<object> {
1166
+ const needCommit = env ? false : true;
1167
+ env = env ? env : new Environment();
1168
+ const f = async () => {
1169
+ try {
1170
+ await fetchAuthImpl().acceptInvitation(email, tempPassword, newPassword, env);
1171
+ return {
1172
+ email: email,
1173
+ message: 'Invitation accepted successfully',
1174
+ };
1175
+ } catch (err: any) {
1176
+ logger.error(`Accept invitation failed: ${err.message}`);
1177
+ throw err;
1178
+ }
1179
+ };
1180
+ if (needCommit) {
1181
+ return await env.callInTransaction(f);
1182
+ } else {
1183
+ return await f();
1184
+ }
1185
+ }
1186
+
1116
1187
  export function requireAuth(moduleName: string, eventName: string): boolean {
1117
1188
  if (isAuthEnabled()) {
1118
1189
  const f =
@@ -1123,7 +1194,8 @@ export function requireAuth(moduleName: string, eventName: string): boolean {
1123
1194
  eventName == 'resendConfirmationCode' ||
1124
1195
  eventName == 'forgotPassword' ||
1125
1196
  eventName == 'confirmForgotPassword' ||
1126
- eventName == 'refreshToken');
1197
+ eventName == 'refreshToken' ||
1198
+ eventName == 'acceptInvitation');
1127
1199
  return !f;
1128
1200
  } else {
1129
1201
  return false;