struere 0.5.15 → 0.5.17

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.
@@ -20096,6 +20096,7 @@ Struere is a framework for building production AI agents with Convex as the real
20096
20096
  \u2502 \u251C\u2500\u2500 agents/*.ts \u2192 Agent configs synced to Convex \u2502
20097
20097
  \u2502 \u251C\u2500\u2500 entity-types/*.ts \u2192 Schema definitions synced to Convex \u2502
20098
20098
  \u2502 \u251C\u2500\u2500 roles/*.ts \u2192 RBAC policies synced to Convex \u2502
20099
+ \u2502 \u251C\u2500\u2500 triggers/*.ts \u2192 Automation rules synced to Convex \u2502
20099
20100
  \u2502 \u251C\u2500\u2500 tools/index.ts \u2192 Custom tools (run on CF Worker) \u2502
20100
20101
  \u2502 \u2514\u2500\u2500 evals/*.eval.yaml \u2192 Test suites (synced + executed) \u2502
20101
20102
  \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u252C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518
@@ -20134,6 +20135,10 @@ roles/ # RBAC: who can do what
20134
20135
  \u251C\u2500\u2500 support.ts # Limited access
20135
20136
  \u2514\u2500\u2500 index.ts
20136
20137
 
20138
+ triggers/ # Automation rules (react to entity changes)
20139
+ \u251C\u2500\u2500 notify-on-create.ts
20140
+ \u2514\u2500\u2500 index.ts
20141
+
20137
20142
  tools/ # Custom tools shared by all agents
20138
20143
  \u2514\u2500\u2500 index.ts # defineTools([...])
20139
20144
 
@@ -20174,6 +20179,7 @@ export default defineConfig({
20174
20179
  | \`struere add agent <name>\` | Create agents/name.ts with template |
20175
20180
  | \`struere add entity-type <name>\` | Create entity-types/name.ts |
20176
20181
  | \`struere add role <name>\` | Create roles/name.ts |
20182
+ | \`struere add trigger <name>\` | Create triggers/name.ts with template |
20177
20183
  | \`struere add eval <name>\` | Create evals/name.eval.yaml with template |
20178
20184
  | \`struere eval\` | Run all eval suites |
20179
20185
  | \`struere eval --suite <name>\` | Run a specific eval suite |
@@ -20273,6 +20279,39 @@ export default defineEntityType({
20273
20279
  })
20274
20280
  \`\`\`
20275
20281
 
20282
+ ### Binding Entity Types to Roles (\`boundToRole\`)
20283
+
20284
+ When an entity type represents users of a specific role (e.g. a "teacher" entity type for users with the "teacher" role), declare the binding explicitly:
20285
+
20286
+ \`\`\`typescript
20287
+ import { defineEntityType } from 'struere'
20288
+
20289
+ export default defineEntityType({
20290
+ name: "Teacher",
20291
+ slug: "teacher",
20292
+ schema: {
20293
+ type: "object",
20294
+ properties: {
20295
+ name: { type: "string" },
20296
+ email: { type: "string", format: "email" },
20297
+ userId: { type: "string" }, // Links to the Convex user _id
20298
+ },
20299
+ required: ["name", "email", "userId"],
20300
+ },
20301
+ searchFields: ["name", "email"],
20302
+ boundToRole: "teacher", // Role name this entity type represents
20303
+ userIdField: "userId", // Field in data that holds the user's Convex _id (defaults to "userId")
20304
+ })
20305
+ \`\`\`
20306
+
20307
+ This enables \`actor.entityId\` in scope rules to automatically resolve which entity record belongs to the current user. Without \`boundToRole\`, scope rules using \`actor.entityId\` will match nothing.
20308
+
20309
+ **Rules:**
20310
+ - Each role can only be bound to one entity type (enforced at sync time)
20311
+ - \`userIdField\` defaults to \`"userId"\` if not specified
20312
+ - \`userIdField\` requires \`boundToRole\` to be set
20313
+ - The entity's \`data[userIdField]\` must contain the Convex user \`_id\`
20314
+
20276
20315
  Entities are stored as:
20277
20316
  \`\`\`json
20278
20317
  {
@@ -20307,7 +20346,7 @@ export default defineRole({
20307
20346
  entityType: "customer",
20308
20347
  field: "data.assignedTo", // Field in entity data
20309
20348
  operator: "eq",
20310
- value: "actor.userId" // Current user's ID
20349
+ value: "actor.userId" // Current user's Convex _id
20311
20350
  },
20312
20351
  ],
20313
20352
 
@@ -20328,6 +20367,45 @@ Every tool call goes through permission checks:
20328
20367
 
20329
20368
  Deny policies always override allow.
20330
20369
 
20370
+ ### Scope Rule Values
20371
+
20372
+ | Value | Resolves To |
20373
+ |-------|-------------|
20374
+ | \`"actor.userId"\` | Current user's Convex \`_id\` |
20375
+ | \`"actor.organizationId"\` | Current organization ID |
20376
+ | \`"actor.entityId"\` | Entity record bound to this user via \`boundToRole\` (requires entity type with \`boundToRole\` set) |
20377
+ | \`"actor.relatedIds:TYPE"\` | IDs of entities related to the actor's entity via relation type TYPE |
20378
+ | \`"literal:VALUE"\` | Literal string value |
20379
+
20380
+ **Example using \`actor.entityId\`:**
20381
+
20382
+ \`\`\`typescript
20383
+ // roles/teacher.ts \u2014 Teacher sees only their own sessions
20384
+ export default defineRole({
20385
+ name: "teacher",
20386
+ policies: [
20387
+ { resource: "session", actions: ["list", "read", "update"], effect: "allow" },
20388
+ ],
20389
+ scopeRules: [
20390
+ {
20391
+ entityType: "session",
20392
+ field: "data.teacherId",
20393
+ operator: "eq",
20394
+ value: "actor.entityId" // Resolves to the teacher entity _id for this user
20395
+ },
20396
+ ],
20397
+ })
20398
+
20399
+ // entity-types/teacher.ts \u2014 Must declare boundToRole for actor.entityId to work
20400
+ export default defineEntityType({
20401
+ name: "Teacher",
20402
+ slug: "teacher",
20403
+ boundToRole: "teacher",
20404
+ userIdField: "userId",
20405
+ // ...schema
20406
+ })
20407
+ \`\`\`
20408
+
20331
20409
  ## Defining Custom Tools
20332
20410
 
20333
20411
  Edit \`tools/index.ts\`:
@@ -20396,6 +20474,103 @@ Custom tool handlers can only fetch from:
20396
20474
  - api.stripe.com, api.sendgrid.com, api.twilio.com
20397
20475
  - hooks.slack.com, discord.com, api.github.com
20398
20476
 
20477
+ ## Defining Triggers
20478
+
20479
+ Triggers are automated actions that fire when entities are created, updated, or deleted. They run asynchronously after the mutation completes.
20480
+
20481
+ Create \\\`triggers/notify-on-session.ts\\\`:
20482
+ \\\`\\\`\\\`typescript
20483
+ import { defineTrigger } from 'struere'
20484
+
20485
+ export default defineTrigger({
20486
+ name: "Notify Teacher on New Session",
20487
+ slug: "notify-teacher-on-session",
20488
+ on: {
20489
+ entityType: "session",
20490
+ action: "created",
20491
+ condition: { "data.status": "scheduled" } // optional
20492
+ },
20493
+ actions: [
20494
+ {
20495
+ tool: "entity.get",
20496
+ args: { id: "{{trigger.data.teacherId}}" },
20497
+ as: "teacher" // store result for later steps
20498
+ },
20499
+ {
20500
+ tool: "event.emit",
20501
+ args: {
20502
+ eventType: "notification.sent",
20503
+ entityId: "{{trigger.entityId}}",
20504
+ payload: {
20505
+ teacherName: "{{steps.teacher.data.name}}",
20506
+ sessionTime: "{{trigger.data.startTime}}"
20507
+ }
20508
+ }
20509
+ }
20510
+ ]
20511
+ })
20512
+ \\\`\\\`\\\`
20513
+
20514
+ ### Trigger Config
20515
+
20516
+ | Field | Required | Description |
20517
+ |-------|----------|-------------|
20518
+ | \\\`name\\\` | Yes | Display name |
20519
+ | \\\`slug\\\` | Yes | Unique identifier (used for sync) |
20520
+ | \\\`on.entityType\\\` | Yes | Entity type slug to watch |
20521
+ | \\\`on.action\\\` | Yes | \\\`"created"\\\`, \\\`"updated"\\\`, or \\\`"deleted"\\\` |
20522
+ | \\\`on.condition\\\` | No | Dot-notation equality conditions on entity data |
20523
+ | \\\`actions\\\` | Yes | Ordered list of tool calls to execute |
20524
+
20525
+ ### Action Pipeline
20526
+
20527
+ Each action in the \\\`actions\\\` array runs sequentially:
20528
+
20529
+ | Field | Required | Description |
20530
+ |-------|----------|-------------|
20531
+ | \\\`tool\\\` | Yes | Built-in or custom tool name |
20532
+ | \\\`args\\\` | Yes | Arguments (supports \\\`{{template}}\\\` variables) |
20533
+ | \\\`as\\\` | No | Store result under this name for later steps |
20534
+
20535
+ ### Template Variables
20536
+
20537
+ Use \\\`{{...}}\\\` in action args to reference trigger context and previous step results:
20538
+
20539
+ | Variable | Description |
20540
+ |----------|-------------|
20541
+ | \\\`{{trigger.entityId}}\\\` | ID of the entity that changed |
20542
+ | \\\`{{trigger.entityType}}\\\` | Entity type slug |
20543
+ | \\\`{{trigger.action}}\\\` | \\\`"created"\\\`, \\\`"updated"\\\`, or \\\`"deleted"\\\` |
20544
+ | \\\`{{trigger.data.X}}\\\` | Entity data field (e.g. \\\`{{trigger.data.email}}\\\`) |
20545
+ | \\\`{{trigger.previousData.X}}\\\` | Previous value (updates/deletes only) |
20546
+ | \\\`{{steps.NAME.X}}\\\` | Result from a prior action with \\\`as: "NAME"\\\` |
20547
+
20548
+ ### Conditions
20549
+
20550
+ Conditions use dot-notation path equality matching:
20551
+ \\\`\\\`\\\`typescript
20552
+ condition: {
20553
+ "data.status": "scheduled", // field must equal value
20554
+ "data.priority": "high" // all conditions must match (AND)
20555
+ }
20556
+ \\\`\\\`\\\`
20557
+
20558
+ ### Execution Model
20559
+
20560
+ - **Async**: Triggers are scheduled after the mutation, keeping writes fast
20561
+ - **System actor**: Actions execute with full admin access (not the user's role)
20562
+ - **Fail-fast**: If any action fails, remaining actions are skipped
20563
+ - **Events**: Emits \\\`trigger.executed\\\` on success, \\\`trigger.failed\\\` on error
20564
+ - **Both paths**: Triggers fire from dashboard CRUD, agent tool calls, and API mutations
20565
+
20566
+ ### Scaffold a new trigger
20567
+
20568
+ \\\`\\\`\\\`bash
20569
+ struere add trigger my-trigger
20570
+ \\\`\\\`\\\`
20571
+
20572
+ Creates \\\`triggers/my-trigger.ts\\\` with a starter template.
20573
+
20399
20574
  ## Built-in Tools Reference
20400
20575
 
20401
20576
  ### Entity Tools
@@ -20452,6 +20627,93 @@ Events are immutable audit logs. Use for analytics, debugging, compliance.
20452
20627
 
20453
20628
  Jobs run asynchronously with retry logic. Use for: emails, notifications, data sync.
20454
20629
 
20630
+ ### Calendar Tools
20631
+
20632
+ Requires a connected Google Calendar (via Settings > Integrations). The \`userId\` must be a user with a linked Google account.
20633
+
20634
+ \`\`\`typescript
20635
+ // calendar.list - List events in a time range
20636
+ { userId: "user_abc", timeMin: "2024-01-01T00:00:00Z", timeMax: "2024-01-31T23:59:59Z", maxResults: 50 }
20637
+
20638
+ // calendar.create - Create a calendar event
20639
+ {
20640
+ userId: "user_abc",
20641
+ summary: "Session with Student",
20642
+ startTime: "2024-01-15T10:00:00Z",
20643
+ endTime: "2024-01-15T11:00:00Z",
20644
+ description: "Weekly tutoring session", // optional
20645
+ attendees: ["student@example.com"], // optional
20646
+ timeZone: "America/New_York" // optional
20647
+ }
20648
+
20649
+ // calendar.update - Update an existing event
20650
+ { userId: "user_abc", eventId: "evt_123", summary: "Updated title", startTime: "2024-01-15T11:00:00Z" }
20651
+
20652
+ // calendar.delete - Delete a calendar event
20653
+ { userId: "user_abc", eventId: "evt_123" }
20654
+
20655
+ // calendar.freeBusy - Check availability
20656
+ { userId: "user_abc", timeMin: "2024-01-15T00:00:00Z", timeMax: "2024-01-15T23:59:59Z" }
20657
+ // Returns: { busy: [{ start: "...", end: "..." }, ...] }
20658
+ \`\`\`
20659
+
20660
+ Calendar tools work in both agent tool calls and trigger actions. Example trigger:
20661
+ \`\`\`typescript
20662
+ defineTrigger({
20663
+ name: "Add Session to Calendar",
20664
+ slug: "add-session-to-calendar",
20665
+ on: { entityType: "session", action: "created" },
20666
+ actions: [
20667
+ {
20668
+ tool: "calendar.create",
20669
+ args: {
20670
+ userId: "{{trigger.data.teacherId}}",
20671
+ summary: "Session: {{trigger.data.studentName}}",
20672
+ startTime: "{{trigger.data.startTime}}",
20673
+ endTime: "{{trigger.data.endTime}}"
20674
+ },
20675
+ as: "calendarEvent"
20676
+ }
20677
+ ]
20678
+ })
20679
+ \`\`\`
20680
+
20681
+ ### Agent Communication Tools
20682
+
20683
+ The \\\`agent.chat\\\` tool lets agents delegate work to other agents within the same organization and environment. The calling agent sends a message to a target agent (by slug), waits for its response, and uses the result to continue its own execution.
20684
+
20685
+ \`\`\`typescript
20686
+ // agent.chat - Send a message to another agent
20687
+ { agent: "billing-agent", message: "How many credits does guardian ent_abc have?", context: { guardianId: "ent_abc" } }
20688
+ // Returns: { response: "Guardian has 5 remaining credits...", threadId: "thread_xyz", agentSlug: "billing-agent", usage: {...} }
20689
+ \`\`\`
20690
+
20691
+ **Safety limits:**
20692
+ - **Depth limit**: Max 3 levels of agent-to-agent delegation (A\u2192B\u2192C allowed, A\u2192B\u2192C\u2192D rejected)
20693
+ - **Cycle detection**: An agent cannot call itself
20694
+ - **Per-agent iteration cap**: Each agent's LLM loop is independently limited to 10 steps
20695
+
20696
+ **How it works:**
20697
+ 1. Caller agent invokes \\\`agent.chat\\\` with a target slug and message
20698
+ 2. Target agent is resolved by slug within the same organization
20699
+ 3. A new thread is created for the target agent (linked via shared \\\`conversationId\\\`)
20700
+ 4. Target agent runs its own LLM loop with its own system prompt, tools, and permissions
20701
+ 5. Response is returned as a tool result to the calling agent
20702
+
20703
+ **Example: Coordinator delegates to specialist**
20704
+ \`\`\`typescript
20705
+ // agents/coordinator.ts
20706
+ export default defineAgent({
20707
+ name: "Coordinator",
20708
+ slug: "coordinator",
20709
+ version: "0.1.0",
20710
+ systemPrompt: \\\\\\\`You coordinate between specialist agents.
20711
+ When asked about billing, delegate to the billing agent using agent.chat.
20712
+ When asked about scheduling, delegate to the scheduler agent.\\\\\\\`,
20713
+ tools: ["agent.chat", "entity.query", "event.emit"],
20714
+ })
20715
+ \`\`\`
20716
+
20455
20717
  ## Invoking Agents (API)
20456
20718
 
20457
20719
  ### Chat Endpoint
@@ -20654,6 +20916,29 @@ Always confirm what was created/updated.\\\`,
20654
20916
  \`\`\`
20655
20917
  `;
20656
20918
  }
20919
+ function getTriggerTs(name, slug) {
20920
+ return `import { defineTrigger } from 'struere'
20921
+
20922
+ export default defineTrigger({
20923
+ name: "${name}",
20924
+ slug: "${slug}",
20925
+ on: {
20926
+ entityType: "ENTITY_TYPE_HERE",
20927
+ action: "created",
20928
+ },
20929
+ actions: [
20930
+ {
20931
+ tool: "event.emit",
20932
+ args: {
20933
+ eventType: "trigger.${slug}.fired",
20934
+ entityId: "{{trigger.entityId}}",
20935
+ payload: { triggeredBy: "${slug}" },
20936
+ },
20937
+ },
20938
+ ],
20939
+ })
20940
+ `;
20941
+ }
20657
20942
 
20658
20943
  // src/cli/utils/scaffold.ts
20659
20944
  function ensureDir(filePath) {
@@ -20677,7 +20962,8 @@ function scaffoldProjectV2(cwd, options) {
20677
20962
  "entity-types",
20678
20963
  "roles",
20679
20964
  "tools",
20680
- "evals"
20965
+ "evals",
20966
+ "triggers"
20681
20967
  ];
20682
20968
  for (const dir of directories) {
20683
20969
  const dirPath = join3(cwd, dir);
@@ -20697,6 +20983,7 @@ function scaffoldProjectV2(cwd, options) {
20697
20983
  "entity-types/index.ts": getIndexTs("entity-types"),
20698
20984
  "roles/index.ts": getIndexTs("roles"),
20699
20985
  "tools/index.ts": getToolsIndexTs(),
20986
+ "triggers/index.ts": getIndexTs("triggers"),
20700
20987
  "evals/basic-agent-tests.eval.yaml": getExampleEvalYaml("my-agent")
20701
20988
  };
20702
20989
  for (const [relativePath, content] of Object.entries(files)) {
@@ -20782,6 +21069,24 @@ function scaffoldEval(cwd, name, slug, agentSlug = "my-agent") {
20782
21069
  result.createdFiles.push(`evals/${fileName}`);
20783
21070
  return result;
20784
21071
  }
21072
+ function scaffoldTrigger(cwd, name, slug) {
21073
+ const result = {
21074
+ createdFiles: [],
21075
+ updatedFiles: []
21076
+ };
21077
+ const triggersDir = join3(cwd, "triggers");
21078
+ if (!existsSync3(triggersDir)) {
21079
+ mkdirSync2(triggersDir, { recursive: true });
21080
+ }
21081
+ const fileName = `${slug}.ts`;
21082
+ const filePath = join3(triggersDir, fileName);
21083
+ if (existsSync3(filePath)) {
21084
+ return result;
21085
+ }
21086
+ writeFileSync3(filePath, getTriggerTs(name, slug));
21087
+ result.createdFiles.push(`triggers/${fileName}`);
21088
+ return result;
21089
+ }
20785
21090
 
20786
21091
  // src/cli/commands/init.ts
20787
21092
  async function runInit(cwd, selectedOrg) {
@@ -20985,7 +21290,8 @@ async function loadAllResources(cwd) {
20985
21290
  }
20986
21291
  const { suites: evalSuites, errors: evalErrors } = loadAllEvalSuites(join4(cwd, "evals"));
20987
21292
  errors2.push(...evalErrors);
20988
- return { agents, entityTypes, roles, customTools, evalSuites, errors: errors2 };
21293
+ const triggers = await loadAllTriggers(join4(cwd, "triggers"));
21294
+ return { agents, entityTypes, roles, customTools, evalSuites, triggers, errors: errors2 };
20989
21295
  }
20990
21296
  async function loadAllAgents(dir) {
20991
21297
  if (!existsSync4(dir)) {
@@ -21093,13 +21399,24 @@ function loadAllEvalSuites(dir) {
21093
21399
  }
21094
21400
  return { suites, errors: errors2 };
21095
21401
  }
21402
+ async function loadAllTriggers(dir) {
21403
+ if (!existsSync4(dir)) {
21404
+ return [];
21405
+ }
21406
+ const indexPath = join4(dir, "index.ts");
21407
+ if (existsSync4(indexPath)) {
21408
+ return loadFromIndex(indexPath);
21409
+ }
21410
+ return loadFromDirectory(dir);
21411
+ }
21096
21412
  function getResourceDirectories(cwd) {
21097
21413
  return {
21098
21414
  agents: join4(cwd, "agents"),
21099
21415
  entityTypes: join4(cwd, "entity-types"),
21100
21416
  roles: join4(cwd, "roles"),
21101
21417
  tools: join4(cwd, "tools"),
21102
- evals: join4(cwd, "evals")
21418
+ evals: join4(cwd, "evals"),
21419
+ triggers: join4(cwd, "triggers")
21103
21420
  };
21104
21421
  }
21105
21422
 
@@ -21122,7 +21439,13 @@ var BUILTIN_TOOLS = [
21122
21439
  "event.emit",
21123
21440
  "event.query",
21124
21441
  "job.enqueue",
21125
- "job.status"
21442
+ "job.status",
21443
+ "calendar.list",
21444
+ "calendar.create",
21445
+ "calendar.update",
21446
+ "calendar.delete",
21447
+ "calendar.freeBusy",
21448
+ "agent.chat"
21126
21449
  ];
21127
21450
  function extractSyncPayload(resources) {
21128
21451
  const customToolsMap = new Map;
@@ -21135,7 +21458,9 @@ function extractSyncPayload(resources) {
21135
21458
  slug: et.slug,
21136
21459
  schema: et.schema,
21137
21460
  searchFields: et.searchFields,
21138
- displayConfig: et.displayConfig
21461
+ displayConfig: et.displayConfig,
21462
+ boundToRole: et.boundToRole,
21463
+ userIdField: et.userIdField
21139
21464
  }));
21140
21465
  const roles = resources.roles.map((role) => ({
21141
21466
  name: role.name,
@@ -21178,7 +21503,20 @@ function extractSyncPayload(resources) {
21178
21503
  finalAssertions: c.finalAssertions
21179
21504
  }))
21180
21505
  })) : undefined;
21181
- return { agents, entityTypes, roles, evalSuites };
21506
+ const triggers = resources.triggers.length > 0 ? resources.triggers.map((t) => ({
21507
+ name: t.name,
21508
+ slug: t.slug,
21509
+ description: t.description,
21510
+ entityType: t.on.entityType,
21511
+ action: t.on.action,
21512
+ condition: t.on.condition,
21513
+ actions: t.actions.map((a) => ({
21514
+ tool: a.tool,
21515
+ args: a.args,
21516
+ as: a.as
21517
+ }))
21518
+ })) : undefined;
21519
+ return { agents, entityTypes, roles, evalSuites, triggers };
21182
21520
  }
21183
21521
  function extractAgentPayload(agent, customToolsMap) {
21184
21522
  let systemPrompt;
@@ -21241,7 +21579,13 @@ function getBuiltinToolDescription(name) {
21241
21579
  "event.emit": "Emit a custom event for audit logging",
21242
21580
  "event.query": "Query historical events with optional filters",
21243
21581
  "job.enqueue": "Schedule a background job to run later",
21244
- "job.status": "Get the status of a scheduled job"
21582
+ "job.status": "Get the status of a scheduled job",
21583
+ "calendar.list": "List Google Calendar events for a user within a time range",
21584
+ "calendar.create": "Create a Google Calendar event on a user's calendar",
21585
+ "calendar.update": "Update an existing Google Calendar event",
21586
+ "calendar.delete": "Delete a Google Calendar event",
21587
+ "calendar.freeBusy": "Check free/busy availability on a user's Google Calendar",
21588
+ "agent.chat": "Send a message to another agent and get its response"
21245
21589
  };
21246
21590
  return descriptions[name] || name;
21247
21591
  }
@@ -21347,6 +21691,69 @@ function getBuiltinToolParameters(name) {
21347
21691
  id: { type: "string", description: "The job ID to check" }
21348
21692
  },
21349
21693
  required: ["id"]
21694
+ },
21695
+ "calendar.list": {
21696
+ type: "object",
21697
+ properties: {
21698
+ userId: { type: "string", description: "User ID (Convex or Clerk) whose calendar to query" },
21699
+ timeMin: { type: "string", description: "Start of time range (ISO 8601 datetime)" },
21700
+ timeMax: { type: "string", description: "End of time range (ISO 8601 datetime)" },
21701
+ maxResults: { type: "number", description: "Maximum number of events to return" }
21702
+ },
21703
+ required: ["userId", "timeMin", "timeMax"]
21704
+ },
21705
+ "calendar.create": {
21706
+ type: "object",
21707
+ properties: {
21708
+ userId: { type: "string", description: "User ID (Convex or Clerk) whose calendar to create the event on" },
21709
+ summary: { type: "string", description: "Event title" },
21710
+ startTime: { type: "string", description: "Event start time (ISO 8601 datetime)" },
21711
+ endTime: { type: "string", description: "Event end time (ISO 8601 datetime)" },
21712
+ description: { type: "string", description: "Event description" },
21713
+ attendees: { type: "array", items: { type: "string" }, description: "List of attendee email addresses" },
21714
+ timeZone: { type: "string", description: 'Time zone (e.g., "America/Santiago")' }
21715
+ },
21716
+ required: ["userId", "summary", "startTime", "endTime"]
21717
+ },
21718
+ "calendar.update": {
21719
+ type: "object",
21720
+ properties: {
21721
+ userId: { type: "string", description: "User ID (Convex or Clerk) whose calendar contains the event" },
21722
+ eventId: { type: "string", description: "Google Calendar event ID to update" },
21723
+ summary: { type: "string", description: "New event title" },
21724
+ startTime: { type: "string", description: "New start time (ISO 8601 datetime)" },
21725
+ endTime: { type: "string", description: "New end time (ISO 8601 datetime)" },
21726
+ description: { type: "string", description: "New event description" },
21727
+ attendees: { type: "array", items: { type: "string" }, description: "Updated list of attendee emails" },
21728
+ status: { type: "string", description: "Event status (confirmed, tentative, cancelled)" }
21729
+ },
21730
+ required: ["userId", "eventId"]
21731
+ },
21732
+ "calendar.delete": {
21733
+ type: "object",
21734
+ properties: {
21735
+ userId: { type: "string", description: "User ID (Convex or Clerk) whose calendar contains the event" },
21736
+ eventId: { type: "string", description: "Google Calendar event ID to delete" }
21737
+ },
21738
+ required: ["userId", "eventId"]
21739
+ },
21740
+ "calendar.freeBusy": {
21741
+ type: "object",
21742
+ properties: {
21743
+ userId: { type: "string", description: "User ID (Convex or Clerk) whose availability to check" },
21744
+ timeMin: { type: "string", description: "Start of time range (ISO 8601 datetime)" },
21745
+ timeMax: { type: "string", description: "End of time range (ISO 8601 datetime)" }
21746
+ },
21747
+ required: ["userId", "timeMin", "timeMax"]
21748
+ },
21749
+ "agent.chat": {
21750
+ type: "object",
21751
+ properties: {
21752
+ agent: { type: "string", description: "Target agent slug to communicate with" },
21753
+ message: { type: "string", description: "The message to send to the agent" },
21754
+ context: { type: "object", description: "Optional context data to pass to the target agent" }
21755
+ },
21756
+ required: ["agent", "message"]
21350
21757
  }
21351
21758
  };
21352
21759
  return schemas[name] || { type: "object", properties: {} };
@@ -21485,7 +21892,7 @@ var devCommand = new Command("dev").description("Sync all resources to developme
21485
21892
  spinner.start("Loading resources");
21486
21893
  try {
21487
21894
  const resources = await loadAllResources(cwd);
21488
- spinner.succeed(`Loaded ${resources.agents.length} agents, ${resources.entityTypes.length} entity types, ${resources.roles.length} roles, ${resources.customTools.length} custom tools, ${resources.evalSuites.length} eval suites`);
21895
+ spinner.succeed(`Loaded ${resources.agents.length} agents, ${resources.entityTypes.length} entity types, ${resources.roles.length} roles, ${resources.customTools.length} custom tools, ${resources.evalSuites.length} eval suites, ${resources.triggers.length} triggers`);
21489
21896
  for (const err of resources.errors) {
21490
21897
  console.log(source_default.red(" \u2716"), err);
21491
21898
  }
@@ -21544,6 +21951,7 @@ var devCommand = new Command("dev").description("Sync all resources to developme
21544
21951
  dirs.roles,
21545
21952
  dirs.tools,
21546
21953
  dirs.evals,
21954
+ dirs.triggers,
21547
21955
  join5(cwd, "struere.config.ts")
21548
21956
  ].filter((p) => existsSync5(p));
21549
21957
  const watcher = import_chokidar.default.watch(watchPaths, {
@@ -22513,6 +22921,17 @@ var addCommand = new Command("add").description("Scaffold a new resource").argum
22513
22921
  console.log(source_default.yellow("Eval suite already exists:"), `evals/${slug}.eval.yaml`);
22514
22922
  }
22515
22923
  break;
22924
+ case "trigger":
22925
+ result = scaffoldTrigger(cwd, displayName, slug);
22926
+ if (result.createdFiles.length > 0) {
22927
+ console.log(source_default.green("\u2713"), `Created trigger "${displayName}"`);
22928
+ for (const file of result.createdFiles) {
22929
+ console.log(source_default.gray(" \u2192"), file);
22930
+ }
22931
+ } else {
22932
+ console.log(source_default.yellow("Trigger already exists:"), `triggers/${slug}.ts`);
22933
+ }
22934
+ break;
22516
22935
  default:
22517
22936
  console.log(source_default.red("Unknown resource type:"), type);
22518
22937
  console.log();
@@ -22521,6 +22940,7 @@ var addCommand = new Command("add").description("Scaffold a new resource").argum
22521
22940
  console.log(source_default.gray(" -"), source_default.cyan("entity-type"), "- Create an entity type schema");
22522
22941
  console.log(source_default.gray(" -"), source_default.cyan("role"), "- Create a role with permissions");
22523
22942
  console.log(source_default.gray(" -"), source_default.cyan("eval"), "- Create an eval suite (YAML)");
22943
+ console.log(source_default.gray(" -"), source_default.cyan("trigger"), "- Create an entity trigger");
22524
22944
  console.log();
22525
22945
  process.exit(1);
22526
22946
  }
@@ -23076,7 +23496,7 @@ var pullCommand = new Command("pull").description("Pull remote resources to loca
23076
23496
  // package.json
23077
23497
  var package_default = {
23078
23498
  name: "struere",
23079
- version: "0.5.15",
23499
+ version: "0.5.17",
23080
23500
  description: "Build, test, and deploy AI agents",
23081
23501
  keywords: [
23082
23502
  "ai",
@@ -1 +1 @@
1
- {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAMnC,eAAO,MAAM,UAAU,SAwGnB,CAAA"}
1
+ {"version":3,"file":"add.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/add.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAMnC,eAAO,MAAM,UAAU,SAqHnB,CAAA"}
@@ -1 +1 @@
1
- {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/dev.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAenC,eAAO,MAAM,UAAU,SA+RnB,CAAA"}
1
+ {"version":3,"file":"dev.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/dev.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAA;AAenC,eAAO,MAAM,UAAU,SAgSnB,CAAA"}