@vantageos/agent-engine 0.1.0-alpha.4 → 0.1.0-alpha.5

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/README.md CHANGED
@@ -214,10 +214,31 @@ Two gates now exist, and they check different things:
214
214
  hand-edit of existing generated content.**
215
215
  2. **Content, requires a live deployment** --
216
216
  `scripts/check-generated-content.mjs` (`npm run
217
- check:generated:content`). Actually runs `npx convex codegen` against
218
- the dev deployment and diffs `convex/_generated/` byte-for-byte
219
- (`git diff --exit-code`). Catches everything, including a single hand-
220
- edited character.
217
+ check:generated:content`). Runs `npx convex codegen` against the dev
218
+ deployment for BOTH `convex/_generated/` (the host repo's own dev
219
+ deployment bindings) AND, since 0.1.0-alpha.5, `component/_generated/`
220
+ via `npx convex codegen --component-dir component` -- the checked-in
221
+ file `tsc -p tsconfig.build.json` copies verbatim into the PUBLISHED
222
+ `dist/component/_generated/component.d.ts`, i.e. the exact type a
223
+ consuming app checks `components.agentEngine.*` calls against. Diffs
224
+ each byte-for-byte (`git diff --exit-code`). Catches everything,
225
+ including a single hand-edited character.
226
+
227
+ **`component/_generated/` was NOT covered before 0.1.0-alpha.5** — that
228
+ gap is why `0.1.0-alpha.4` shipped a `component.d.ts` stale against the
229
+ real component schema (missing `orgId`/`createdBy` on
230
+ sessions/agents/durableJob/toolApprovals args), which broke
231
+ `tsc --noEmit` for any consumer calling `sessions.start`/`durableJob.start`
232
+ with `orgId` (Hestia, VP k17ea6x3tht8xa3rxm139t23gx8ajkf3, defaut 1).
233
+ The visibility (`"internal"`) on every entry is, by measured comparison
234
+ against `@convex-dev/rate-limiter`'s own published `component.d.ts`,
235
+ CORRECT and unrelated to that defect — every official Convex component
236
+ types its cross-component `ComponentApi` as `"internal"` regardless of
237
+ whether the underlying function is declared `mutation`/`query` or
238
+ `internalMutation`/`internalQuery`; `ctx.runMutation`'s own signature
239
+ (`convex/server`) accepts `FunctionReference<"mutation", "public" |
240
+ "internal">` either way. The root cause was staleness of the args
241
+ shape, not visibility.
221
242
 
222
243
  **Three-state exit contract** (never two -- a two-state gate cannot
223
244
  distinguish "I measured, it's clean" from "I could not measure at
@@ -389,14 +410,43 @@ components.agentEngine.engine.resolvers.resolveManifest({ orgId, sessionId }) ->
389
410
  components.agentEngine.engine.sessions.start({ orgId, agentId, createdBy }) -> Id<"sessions">
390
411
  components.agentEngine.engine.sessions.sendTurn({ orgId, sessionId, input }) -> null
391
412
  components.agentEngine.engine.sessions.appendReasoning({ orgId, sessionId, chunk }) -> null
392
- components.agentEngine.engine.sessions.requestToolCall({ orgId, sessionId, toolName, argsJson }) -> { authorized, approvalPending }
413
+ components.agentEngine.engine.sessions.requestToolCall({ orgId, sessionId, toolName, argsJson }) -> { authorized, approvalPending, denied }
393
414
  components.agentEngine.engine.sessions.completeToolCall({ orgId, sessionId, toolName, resultJson }) -> null
394
415
  components.agentEngine.engine.sessions.completeTurn({ orgId, sessionId }) -> null
395
416
  components.agentEngine.engine.sessions.listEvents({ orgId, sessionId }) -> sessionEvent[]
396
417
 
397
- components.agentEngine.engine.toolApprovals.respond({ orgId, sessionId, toolName, approval: "always"|"once"|"never" }) -> null
418
+ components.agentEngine.engine.toolApprovals.respond({ orgId, sessionId, toolName, decision: "approved"|"denied", updatePolicy?: "always"|"once"|"never" }) -> null
398
419
  ```
399
420
 
421
+ ### `toolApprovals.respond` — `decision` vs `updatePolicy` (0.1.0-alpha.5)
422
+
423
+ Two DISTINCT axes were conflated pre-alpha.5 (VP
424
+ k17ea6x3tht8xa3rxm139t23gx8ajkf3, defaut 2) — a single `approval:
425
+ "always"|"once"|"never"` argument was overloaded to mean both "the standing
426
+ policy for this tool" AND "the answer to this one pending request", and
427
+ there was no literal for "refuse this turn only". Fixed by splitting them:
428
+
429
+ - **`decision`** (required) — the response to THIS ONE pending
430
+ `tool.approval.requested` event, for THIS turn only. `"approved"` |
431
+ `"denied"`. **Eve parity, direct** — vocabulary matches Eve's
432
+ `ApprovalStatus` (`packages/eve/src/public/definitions/approval.ts`:
433
+ `"approved" | "denied" | "not-applicable" | "user-approval" | ...`); we
434
+ did not invent a `deny` literal, we aligned with the one Eve already has.
435
+ `"denied"` is a terminal refusal for the turn: `sessions.requestToolCall`
436
+ reports back `{ authorized: false, approvalPending: false, denied: true
437
+ }` (never re-authorized on retry within the session), and a dedicated
438
+ `turn.toolCall.denied` sessionEvent is appended — there is NO
439
+ `turn.toolCall.completed` for a denied call, i.e. the tool was never run
440
+ and the turn is NOT silently treated as done.
441
+ - **`updatePolicy`** (optional) — persists a NEW standing gate
442
+ (`"always"|"once"|"never"`, eve parity: `eve/tools/approval` helpers) for
443
+ this `(agentId, toolName)` pair going forward, independent of `decision`.
444
+ A human can deny this turn's call and leave the policy alone (ask again
445
+ next time), or approve this turn and simultaneously flip the policy to
446
+ `"always"`/`"never"` so future calls stop pausing. `"never"` still means
447
+ Eve's "never require approval" (auto-authorize) — that policy literal is
448
+ unchanged and is NOT the same thing as a `"denied"` decision.
449
+
400
450
  `createdBy` (on `agents.create` / `sessions.start`) is, for the same
401
451
  platform reason as `orgId`, no longer read from `ctx.auth.getUserIdentity()`
402
452
  — the consumer passes the acting user's own identifier.
@@ -22,12 +22,15 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
22
22
  engine: {
23
23
  agents: {
24
24
  create: FunctionReference<"mutation", "internal", {
25
+ createdBy: string;
25
26
  instructions: string;
26
27
  model: string;
27
28
  name: string;
29
+ orgId: string;
28
30
  }, string, Name>;
29
31
  get: FunctionReference<"query", "internal", {
30
32
  agentId: string;
33
+ orgId: string;
31
34
  }, {
32
35
  _creationTime: number;
33
36
  _id: string;
@@ -44,9 +47,11 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
44
47
  durableJob: {
45
48
  cancel: FunctionReference<"mutation", "internal", {
46
49
  jobId: string;
50
+ orgId: string;
47
51
  }, null, Name>;
48
52
  getStatus: FunctionReference<"query", "internal", {
49
53
  jobId: string;
54
+ orgId: string;
50
55
  }, {
51
56
  abandonedAtStep?: number;
52
57
  cursor: number;
@@ -57,12 +62,14 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
57
62
  start: FunctionReference<"mutation", "internal", {
58
63
  kind: string;
59
64
  maxAttemptsPerStep: number;
65
+ orgId: string;
60
66
  stepFunctionHandle: string;
61
67
  totalSteps: number;
62
68
  }, string, Name>;
63
69
  };
64
70
  resolvers: {
65
71
  resolveManifest: FunctionReference<"query", "internal", {
72
+ orgId: string;
66
73
  sessionId: string;
67
74
  }, {
68
75
  agentId: string;
@@ -78,46 +85,57 @@ export type ComponentApi<Name extends string | undefined = string | undefined> =
78
85
  sessions: {
79
86
  appendReasoning: FunctionReference<"mutation", "internal", {
80
87
  chunk: string;
88
+ orgId: string;
81
89
  sessionId: string;
82
90
  }, null, Name>;
83
91
  completeToolCall: FunctionReference<"mutation", "internal", {
92
+ orgId: string;
84
93
  resultJson: string;
85
94
  sessionId: string;
86
95
  toolName: string;
87
96
  }, null, Name>;
88
97
  completeTurn: FunctionReference<"mutation", "internal", {
98
+ orgId: string;
89
99
  sessionId: string;
90
100
  }, null, Name>;
91
101
  listEvents: FunctionReference<"query", "internal", {
102
+ orgId: string;
92
103
  sessionId: string;
93
104
  }, Array<{
94
105
  _creationTime: number;
95
106
  _id: string;
96
107
  createdAt: number;
97
- name: "session.started" | "turn.started" | "turn.reasoning" | "turn.toolCall.requested" | "turn.toolCall.completed" | "turn.completed" | "tool.approval.requested" | "tool.approval.resolved";
108
+ name: "session.started" | "turn.started" | "turn.reasoning" | "turn.toolCall.requested" | "turn.toolCall.completed" | "turn.toolCall.denied" | "turn.completed" | "tool.approval.requested" | "tool.approval.resolved";
98
109
  payload?: string;
99
110
  }>, Name>;
100
111
  requestToolCall: FunctionReference<"mutation", "internal", {
101
112
  argsJson: string;
113
+ orgId: string;
102
114
  sessionId: string;
103
115
  toolName: string;
104
116
  }, {
105
117
  approvalPending: boolean;
106
118
  authorized: boolean;
119
+ denied: boolean;
107
120
  }, Name>;
108
121
  sendTurn: FunctionReference<"mutation", "internal", {
109
122
  input: string;
123
+ orgId: string;
110
124
  sessionId: string;
111
125
  }, null, Name>;
112
126
  start: FunctionReference<"mutation", "internal", {
113
127
  agentId: string;
128
+ createdBy: string;
129
+ orgId: string;
114
130
  }, string, Name>;
115
131
  };
116
132
  toolApprovals: {
117
133
  respond: FunctionReference<"mutation", "internal", {
118
- approval: "always" | "once" | "never";
134
+ decision: "approved" | "denied";
135
+ orgId: string;
119
136
  sessionId: string;
120
137
  toolName: string;
138
+ updatePolicy?: "always" | "once" | "never";
121
139
  }, null, Name>;
122
140
  };
123
141
  };
@@ -21,6 +21,7 @@ export declare const requestToolCall: import("convex/server").RegisteredMutation
21
21
  }, Promise<{
22
22
  authorized: boolean;
23
23
  approvalPending: boolean;
24
+ denied: boolean;
24
25
  }>>;
25
26
  export declare const completeToolCall: import("convex/server").RegisteredMutation<"public", {
26
27
  orgId: string;
@@ -38,7 +39,7 @@ export declare const listEvents: import("convex/server").RegisteredQuery<"public
38
39
  }, Promise<{
39
40
  _id: import("convex/values").GenericId<"sessionEvents">;
40
41
  _creationTime: number;
41
- name: "session.started" | "turn.started" | "turn.reasoning" | "turn.toolCall.requested" | "turn.toolCall.completed" | "turn.completed" | "tool.approval.requested" | "tool.approval.resolved";
42
+ name: "session.started" | "turn.started" | "turn.reasoning" | "turn.toolCall.requested" | "turn.toolCall.completed" | "turn.toolCall.denied" | "turn.completed" | "tool.approval.requested" | "tool.approval.resolved";
42
43
  payload: string | undefined;
43
44
  createdAt: number;
44
45
  }[]>>;
@@ -112,7 +112,15 @@ export const requestToolCall = mutation({
112
112
  toolName: v.string(),
113
113
  argsJson: v.string(),
114
114
  },
115
- returns: v.object({ authorized: v.boolean(), approvalPending: v.boolean() }),
115
+ returns: v.object({
116
+ authorized: v.boolean(),
117
+ approvalPending: v.boolean(),
118
+ // Explicit terminal refusal for THIS turn (VP
119
+ // k17ea6x3tht8xa3rxm139t23gx8ajkf3, defaut 2). Distinct from
120
+ // `approvalPending: false, authorized: true` (approved/never-gated)
121
+ // -- a caller must check this before treating the call as skippable.
122
+ denied: v.boolean(),
123
+ }),
116
124
  handler: async (ctx, args) => {
117
125
  const orgId = requireOrgId(args.orgId);
118
126
  const session = await ctx.db.get(args.sessionId);
@@ -132,7 +140,7 @@ export const requestToolCall = mutation({
132
140
  .unique();
133
141
  const approval = approvalRow?.approval ?? "never";
134
142
  if (approval === "never") {
135
- return { authorized: true, approvalPending: false };
143
+ return { authorized: true, approvalPending: false, denied: false };
136
144
  }
137
145
  if (approval === "always") {
138
146
  await insertEvent(ctx, {
@@ -141,20 +149,35 @@ export const requestToolCall = mutation({
141
149
  name: "tool.approval.requested",
142
150
  payload: JSON.stringify({ toolName: args.toolName }),
143
151
  });
144
- return { authorized: false, approvalPending: true };
152
+ return { authorized: false, approvalPending: true, denied: false };
145
153
  }
146
- // approval === "once": gate unless this tool was already resolved once
147
- // in this session.
154
+ // approval === "once": gate unless this tool's approval was already
155
+ // resolved with `decision: "approved"` in this session. A prior
156
+ // `decision: "denied"` resolution must NOT be treated as
157
+ // authorization (that was the pre-alpha.5 bug -- "outil sauté, tour
158
+ // complété quand même") -- it is reported back as a terminal refusal
159
+ // instead, and the caller re-requests (a fresh human decision) rather
160
+ // than silently proceeding.
148
161
  const events = await ctx.db
149
162
  .query("sessionEvents")
150
163
  .withIndex("by_org_session", (q) => q.eq("orgId", orgId).eq("sessionId", args.sessionId))
151
164
  .collect();
152
- const alreadyResolvedThisSession = events.some((e) => e.name === "tool.approval.resolved" &&
165
+ const resolutionsForTool = events
166
+ .filter((e) => e.name === "tool.approval.resolved" &&
153
167
  e.payload !== undefined &&
154
168
  JSON.parse(e.payload).toolName ===
155
- args.toolName);
156
- if (alreadyResolvedThisSession) {
157
- return { authorized: true, approvalPending: false };
169
+ args.toolName)
170
+ .sort((a, b) => a.createdAt - b.createdAt);
171
+ const latestResolution = resolutionsForTool[resolutionsForTool.length - 1];
172
+ const latestDecision = latestResolution?.payload
173
+ ? JSON.parse(latestResolution.payload)
174
+ .decision
175
+ : undefined;
176
+ if (latestDecision === "approved") {
177
+ return { authorized: true, approvalPending: false, denied: false };
178
+ }
179
+ if (latestDecision === "denied") {
180
+ return { authorized: false, approvalPending: false, denied: true };
158
181
  }
159
182
  await insertEvent(ctx, {
160
183
  orgId,
@@ -162,7 +185,7 @@ export const requestToolCall = mutation({
162
185
  name: "tool.approval.requested",
163
186
  payload: JSON.stringify({ toolName: args.toolName }),
164
187
  });
165
- return { authorized: false, approvalPending: true };
188
+ return { authorized: false, approvalPending: true, denied: false };
166
189
  },
167
190
  });
168
191
  // completeToolCall — the caller invokes this after actually running the
@@ -214,7 +237,7 @@ export const listEvents = query({
214
237
  returns: v.array(v.object({
215
238
  _id: v.id("sessionEvents"),
216
239
  _creationTime: v.number(),
217
- name: v.union(v.literal("session.started"), v.literal("turn.started"), v.literal("turn.reasoning"), v.literal("turn.toolCall.requested"), v.literal("turn.toolCall.completed"), v.literal("turn.completed"), v.literal("tool.approval.requested"), v.literal("tool.approval.resolved")),
240
+ name: v.union(v.literal("session.started"), v.literal("turn.started"), v.literal("turn.reasoning"), v.literal("turn.toolCall.requested"), v.literal("turn.toolCall.completed"), v.literal("turn.toolCall.denied"), v.literal("turn.completed"), v.literal("tool.approval.requested"), v.literal("tool.approval.resolved")),
218
241
  payload: v.optional(v.string()),
219
242
  createdAt: v.number(),
220
243
  })),
@@ -1,6 +1,7 @@
1
1
  export declare const respond: import("convex/server").RegisteredMutation<"public", {
2
+ updatePolicy?: "always" | "once" | "never" | undefined;
2
3
  orgId: string;
3
4
  toolName: string;
4
- approval: "always" | "once" | "never";
5
5
  sessionId: import("convex/values").GenericId<"sessions">;
6
+ decision: "approved" | "denied";
6
7
  }, Promise<null>>;
@@ -2,51 +2,89 @@ import { v } from "convex/values";
2
2
  import { mutation } from "../_generated/server.js";
3
3
  import { assertSameOrg, requireOrgId } from "../lib/auth.js";
4
4
  // engine/toolApprovals — respond to a pending approval request
5
- // (`tool.approval.requested`, raised by `sessions.requestToolCall`). The
6
- // decision (always/once/never) is WRITTEN as the agent's standing policy
7
- // for that tool going forward (upsert on `by_org_agent_tool`), and
8
- // `tool.approval.resolved` is appended so `sessions.requestToolCall`'s
9
- // "once" check (and any replaying client) can see it happened.
5
+ // (`tool.approval.requested`, raised by `sessions.requestToolCall`).
6
+ //
7
+ // TWO DISTINCT AXES (VP k17ea6x3tht8xa3rxm139t23gx8ajkf3, defaut 2 --
8
+ // these were conflated pre-alpha.5, which is the defect this fixes):
9
+ //
10
+ // 1. `decision` -- the RESPONSE to THIS ONE pending approval request,
11
+ // for THIS turn only. Vocabulary aligned with Eve's `ApprovalStatus`
12
+ // (packages/eve/src/public/definitions/approval.ts: "approved" |
13
+ // "denied" | ...) -- eve parity, cited verbatim, not "notre ligne".
14
+ // `"denied"` means: this turn's tool call is refused, full stop. It
15
+ // does NOT change the agent's standing policy for the tool, and it
16
+ // must NOT be interpreted as "tool skipped, turn completed anyway" --
17
+ // it appends a `turn.toolCall.denied` sessionEvent instead of letting
18
+ // the caller silently proceed to `completeToolCall`.
19
+ //
20
+ // 2. `updatePolicy` -- optional, persists a NEW standing policy
21
+ // (always | once | never, eve parity: eve/tools/approval helpers) for
22
+ // this (agent, tool) pair going forward, upserted on
23
+ // `by_org_agent_tool`. Independent of `decision`: a human can deny
24
+ // this turn's call AND leave the policy untouched (ask again next
25
+ // time), or approve this turn AND simultaneously flip the policy to
26
+ // "always"/"never" so future calls don't pause.
10
27
  export const respond = mutation({
11
28
  args: {
12
29
  orgId: v.string(),
13
30
  sessionId: v.id("sessions"),
14
31
  toolName: v.string(),
15
- approval: v.union(v.literal("always"), v.literal("once"), v.literal("never")),
32
+ decision: v.union(v.literal("approved"), v.literal("denied")),
33
+ updatePolicy: v.optional(v.union(v.literal("always"), v.literal("once"), v.literal("never"))),
16
34
  },
17
35
  returns: v.null(),
18
36
  handler: async (ctx, args) => {
19
37
  const orgId = requireOrgId(args.orgId);
20
38
  const session = await ctx.db.get(args.sessionId);
21
39
  assertSameOrg(orgId, session, "Session not found");
22
- const existing = await ctx.db
23
- .query("toolApprovals")
24
- .withIndex("by_org_agent_tool", (q) => q
25
- .eq("orgId", orgId)
26
- .eq("agentId", session.agentId)
27
- .eq("toolName", args.toolName))
28
- .unique();
29
40
  const now = Date.now();
30
- if (existing) {
31
- await ctx.db.patch(existing._id, { approval: args.approval, updatedAt: now });
32
- }
33
- else {
34
- await ctx.db.insert("toolApprovals", {
35
- orgId,
36
- agentId: session.agentId,
37
- toolName: args.toolName,
38
- approval: args.approval,
39
- createdAt: now,
40
- updatedAt: now,
41
- });
41
+ if (args.updatePolicy !== undefined) {
42
+ const existing = await ctx.db
43
+ .query("toolApprovals")
44
+ .withIndex("by_org_agent_tool", (q) => q
45
+ .eq("orgId", orgId)
46
+ .eq("agentId", session.agentId)
47
+ .eq("toolName", args.toolName))
48
+ .unique();
49
+ if (existing) {
50
+ await ctx.db.patch(existing._id, {
51
+ approval: args.updatePolicy,
52
+ updatedAt: now,
53
+ });
54
+ }
55
+ else {
56
+ await ctx.db.insert("toolApprovals", {
57
+ orgId,
58
+ agentId: session.agentId,
59
+ toolName: args.toolName,
60
+ approval: args.updatePolicy,
61
+ createdAt: now,
62
+ updatedAt: now,
63
+ });
64
+ }
42
65
  }
43
66
  await ctx.db.insert("sessionEvents", {
44
67
  orgId,
45
68
  sessionId: args.sessionId,
46
69
  name: "tool.approval.resolved",
47
- payload: JSON.stringify({ toolName: args.toolName, approval: args.approval }),
70
+ payload: JSON.stringify({
71
+ toolName: args.toolName,
72
+ decision: args.decision,
73
+ updatedPolicy: args.updatePolicy ?? null,
74
+ }),
48
75
  createdAt: now,
49
76
  });
77
+ if (args.decision === "denied") {
78
+ // Explicit refusal state for THIS turn's tool call -- never a
79
+ // silent "turn.toolCall.completed". See module doc above.
80
+ await ctx.db.insert("sessionEvents", {
81
+ orgId,
82
+ sessionId: args.sessionId,
83
+ name: "turn.toolCall.denied",
84
+ payload: JSON.stringify({ toolName: args.toolName }),
85
+ createdAt: now,
86
+ });
87
+ }
50
88
  return null;
51
89
  },
52
90
  });
@@ -106,14 +106,14 @@ declare const _default: import("convex/server").SchemaDefinition<{
106
106
  }, {}, {}>;
107
107
  sessionEvents: import("convex/server").TableDefinition<import("convex/values").VObject<{
108
108
  payload?: string | undefined;
109
- name: "session.started" | "turn.started" | "turn.reasoning" | "turn.toolCall.requested" | "turn.toolCall.completed" | "turn.completed" | "tool.approval.requested" | "tool.approval.resolved";
109
+ name: "session.started" | "turn.started" | "turn.reasoning" | "turn.toolCall.requested" | "turn.toolCall.completed" | "turn.toolCall.denied" | "turn.completed" | "tool.approval.requested" | "tool.approval.resolved";
110
110
  orgId: string;
111
111
  createdAt: number;
112
112
  sessionId: import("convex/values").GenericId<"sessions">;
113
113
  }, {
114
114
  orgId: import("convex/values").VString<string, "required">;
115
115
  sessionId: import("convex/values").VId<import("convex/values").GenericId<"sessions">, "required">;
116
- name: import("convex/values").VUnion<"session.started" | "turn.started" | "turn.reasoning" | "turn.toolCall.requested" | "turn.toolCall.completed" | "turn.completed" | "tool.approval.requested" | "tool.approval.resolved", [import("convex/values").VLiteral<"session.started", "required">, import("convex/values").VLiteral<"turn.started", "required">, import("convex/values").VLiteral<"turn.reasoning", "required">, import("convex/values").VLiteral<"turn.toolCall.requested", "required">, import("convex/values").VLiteral<"turn.toolCall.completed", "required">, import("convex/values").VLiteral<"turn.completed", "required">, import("convex/values").VLiteral<"tool.approval.requested", "required">, import("convex/values").VLiteral<"tool.approval.resolved", "required">], "required", never>;
116
+ name: import("convex/values").VUnion<"session.started" | "turn.started" | "turn.reasoning" | "turn.toolCall.requested" | "turn.toolCall.completed" | "turn.toolCall.denied" | "turn.completed" | "tool.approval.requested" | "tool.approval.resolved", [import("convex/values").VLiteral<"session.started", "required">, import("convex/values").VLiteral<"turn.started", "required">, import("convex/values").VLiteral<"turn.reasoning", "required">, import("convex/values").VLiteral<"turn.toolCall.requested", "required">, import("convex/values").VLiteral<"turn.toolCall.completed", "required">, import("convex/values").VLiteral<"turn.toolCall.denied", "required">, import("convex/values").VLiteral<"turn.completed", "required">, import("convex/values").VLiteral<"tool.approval.requested", "required">, import("convex/values").VLiteral<"tool.approval.resolved", "required">], "required", never>;
117
117
  payload: import("convex/values").VString<string | undefined, "optional">;
118
118
  createdAt: import("convex/values").VFloat64<number, "required">;
119
119
  }, "required", "name" | "orgId" | "createdAt" | "sessionId" | "payload">, {
@@ -121,7 +121,14 @@ export default defineSchema({
121
121
  sessionEvents: defineTable({
122
122
  orgId: v.string(),
123
123
  sessionId: v.id("sessions"),
124
- name: v.union(v.literal("session.started"), v.literal("turn.started"), v.literal("turn.reasoning"), v.literal("turn.toolCall.requested"), v.literal("turn.toolCall.completed"), v.literal("turn.completed"), v.literal("tool.approval.requested"), v.literal("tool.approval.resolved")),
124
+ name: v.union(v.literal("session.started"), v.literal("turn.started"), v.literal("turn.reasoning"), v.literal("turn.toolCall.requested"), v.literal("turn.toolCall.completed"),
125
+ // "notre ligne" (VP k17ea6x3tht8xa3rxm139t23gx8ajkf3, defaut 2):
126
+ // explicit terminal state for a one-turn approval REFUSAL, distinct
127
+ // from "turn.toolCall.completed" (tool actually ran). Without this,
128
+ // a denied tool call had no event of its own -- a consumer had no
129
+ // way to tell "refused this turn" from "skipped, turn completed
130
+ // anyway", which is the wrong default Hestia had to code around.
131
+ v.literal("turn.toolCall.denied"), v.literal("turn.completed"), v.literal("tool.approval.requested"), v.literal("tool.approval.resolved")),
125
132
  payload: v.optional(v.string()), // JSON-serialized, free contract per event name
126
133
  createdAt: v.number(),
127
134
  })
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vantageos/agent-engine",
3
- "version": "0.1.0-alpha.4",
3
+ "version": "0.1.0-alpha.5",
4
4
  "description": "Reusable Convex component: durable agent engine. Agent = data aggregate (root sheet + child tables), never compiled code. Built on @convex-dev/workflow + @convex-dev/workpool (+ @convex-dev/agent). Eve parity by design.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",