@pikku/mongodb 0.12.14 → 0.12.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,83 @@
1
1
  # @pikku/mongodb
2
2
 
3
+ ## 0.12.17
4
+
5
+ ### Patch Changes
6
+
7
+ - fd9d834: Stop publishing internals that only their own package or file used. The declarations stay; only the entrypoint re-export is removed, so nothing that imported a name from where it is declared is affected.
8
+ - Updated dependencies [063f43a]
9
+ - Updated dependencies [ce66bf8]
10
+ - Updated dependencies [d0307a8]
11
+ - Updated dependencies [ce66bf8]
12
+ - Updated dependencies [3ad2131]
13
+ - Updated dependencies [b930dca]
14
+ - Updated dependencies [b95e77d]
15
+ - Updated dependencies [fd9d834]
16
+ - Updated dependencies [8978fbd]
17
+ - @pikku/core@0.12.82
18
+
19
+ ## 0.12.16
20
+
21
+ ### Patch Changes
22
+
23
+ - e110c55: Add runtime scoring for AI agents: `pikkuAIScorer` for heuristic grades and
24
+ `pikkuAIJudge` for LLM-judged ones, graded off the request path on two queue
25
+ lanes so a slow judge cannot starve the cheap checks. Grades are sampled
26
+ deterministically per `(run, scorer)` and persisted to `ai_run_score`.
27
+ - Updated dependencies [e110c55]
28
+ - Updated dependencies [e110c55]
29
+ - Updated dependencies [e110c55]
30
+ - Updated dependencies [acc8077]
31
+ - Updated dependencies [905f737]
32
+ - Updated dependencies [3cc6428]
33
+ - Updated dependencies [c524adf]
34
+ - Updated dependencies [e110c55]
35
+ - @pikku/core@0.12.81
36
+
37
+ ## 0.12.15
38
+
39
+ ### Patch Changes
40
+
41
+ - 6dada45: fix(workflow,ai-agent): make a run's owner, entry node and step function authoritative
42
+
43
+ A graph run may only start at a node the graph declared in `meta.entryNodeIds`, and
44
+ the generated `POST /workflow/:workflowName/graph/:nodeId` route that let an HTTP
45
+ caller pick the entry node is gone. `startNode` stays for `PikkuTriggerService`,
46
+ which names a declared entry node anyway.
47
+
48
+ `StepState` now records the `rpcName` the workflow dispatched a step with, and the
49
+ step claim rejects a queue message naming a different function with
50
+ `WorkflowStepFunctionMismatchError` before mutating any status — a step runs under
51
+ the run owner's identity and without the `expose` gate, so the message must not
52
+ choose what runs.
53
+
54
+ `approveStep` takes the caller's session, and the generated status routes and
55
+ streams assert the same `assertWorkflowRunOwner` check: a run started through a
56
+ session may only be read and approved by that session's user. A run with no
57
+ recorded owner (trigger, scheduler, unauthenticated route) has nobody to compare
58
+ against and is still gated by the entrypoint's own `auth`/`permissions`.
59
+
60
+ `AIRunStateService.resolveApproval` is now a compare-and-swap returning whether
61
+ _this_ caller made the claim, and both agent resume paths run a tool only for the
62
+ approvals they claimed — concurrent approvals of one tool call no longer all
63
+ execute it.
64
+
65
+ - Updated dependencies [41c1a95]
66
+ - Updated dependencies [ce96383]
67
+ - Updated dependencies [7e60867]
68
+ - Updated dependencies [f8f1244]
69
+ - Updated dependencies [dcf20cb]
70
+ - Updated dependencies [6512384]
71
+ - Updated dependencies [e3b4c14]
72
+ - Updated dependencies [efd0ed1]
73
+ - Updated dependencies [cba98fb]
74
+ - Updated dependencies [ce96383]
75
+ - Updated dependencies [f8f1244]
76
+ - Updated dependencies [f8f1244]
77
+ - Updated dependencies [6e93a35]
78
+ - Updated dependencies [6dada45]
79
+ - @pikku/core@0.12.80
80
+
3
81
  ## 0.12.14
4
82
 
5
83
  ### Patch Changes
@@ -7,7 +7,6 @@ export { MongoDBDeploymentService } from './mongodb-deployment-service.js';
7
7
  export { MongoDBAIStorageService } from './mongodb-ai-storage-service.js';
8
8
  export { MongoDBAgentRunService } from './mongodb-agent-run-service.js';
9
9
  export { MongoDBSecretService } from './mongodb-secret-service.js';
10
- export type { MongoDBSecretServiceConfig } from './mongodb-secret-service.js';
11
10
  export type { WorkflowRunService } from '@pikku/core/workflow';
12
11
  export type { AgentRunService, AgentRunRow } from '@pikku/core/ai-agent';
13
12
  export { MongoDBSessionStore } from './mongodb-session-store.js';
@@ -1,5 +1,6 @@
1
- import type { AIStorageService, AIRunStateService, CreateRunInput } from '@pikku/core/services';
1
+ import type { AIStorageService, AIRunStateService, CreateRunInput, SaveScoreInput } from '@pikku/core/services';
2
2
  import type { AIThread, AIMessage, AgentRunState } from '@pikku/core/ai-agent';
3
+ import type { AIRunScore } from '@pikku/core/ai-scorer';
3
4
  import type { Db } from 'mongodb';
4
5
  export declare class MongoDBAIStorageService implements AIStorageService, AIRunStateService {
5
6
  private db;
@@ -9,6 +10,7 @@ export declare class MongoDBAIStorageService implements AIStorageService, AIRunS
9
10
  private toolCalls;
10
11
  private workingMemory;
11
12
  private aiRuns;
13
+ private aiRunScores;
12
14
  constructor(db: Db);
13
15
  init(): Promise<void>;
14
16
  createThread(resourceId: string, options?: {
@@ -36,6 +38,8 @@ export declare class MongoDBAIStorageService implements AIStorageService, AIRunS
36
38
  run: AgentRunState;
37
39
  approval: NonNullable<AgentRunState['pendingApprovals']>[number];
38
40
  } | null>;
39
- resolveApproval(toolCallId: string, status: 'approved' | 'denied'): Promise<void>;
41
+ resolveApproval(toolCallId: string, status: 'approved' | 'denied'): Promise<boolean>;
42
+ saveScore(score: SaveScoreInput): Promise<void>;
43
+ getScores(runId: string): Promise<AIRunScore[]>;
40
44
  close(): Promise<void>;
41
45
  }
@@ -6,6 +6,7 @@ export class MongoDBAIStorageService {
6
6
  toolCalls;
7
7
  workingMemory;
8
8
  aiRuns;
9
+ aiRunScores;
9
10
  constructor(db) {
10
11
  this.db = db;
11
12
  }
@@ -18,12 +19,14 @@ export class MongoDBAIStorageService {
18
19
  this.workingMemory =
19
20
  this.db.collection('ai_working_memory');
20
21
  this.aiRuns = this.db.collection('ai_run');
22
+ this.aiRunScores = this.db.collection('ai_run_score');
21
23
  await this.threads.createIndex({ resourceId: 1 });
22
24
  await this.aiMessages.createIndex({ threadId: 1, createdAt: 1 });
23
25
  await this.toolCalls.createIndex({ threadId: 1 });
24
26
  await this.toolCalls.createIndex({ messageId: 1 });
25
27
  await this.workingMemory.createIndex({ id: 1, scope: 1 }, { unique: true });
26
28
  await this.aiRuns.createIndex({ threadId: 1, createdAt: 1 });
29
+ await this.aiRunScores.createIndex({ runId: 1, createdAt: 1 });
27
30
  this.initialized = true;
28
31
  }
29
32
  async createThread(resourceId, options) {
@@ -402,7 +405,34 @@ export class MongoDBAIStorageService {
402
405
  return { run, approval };
403
406
  }
404
407
  async resolveApproval(toolCallId, status) {
405
- await this.toolCalls.updateOne({ _id: toolCallId }, { $set: { approvalStatus: status } });
408
+ // Only the caller that moves the tool call off 'pending' has claimed it.
409
+ const result = await this.toolCalls.updateOne({ _id: toolCallId, approvalStatus: 'pending' }, { $set: { approvalStatus: status } });
410
+ return result.modifiedCount > 0;
411
+ }
412
+ async saveScore(score) {
413
+ await this.aiRunScores.insertOne({
414
+ _id: crypto.randomUUID(),
415
+ runId: score.runId,
416
+ scorerName: score.scorerName,
417
+ score: score.score,
418
+ reason: score.reason ?? null,
419
+ metadata: score.metadata ?? null,
420
+ createdAt: new Date(),
421
+ });
422
+ }
423
+ async getScores(runId) {
424
+ const rows = await this.aiRunScores
425
+ .find({ runId })
426
+ .sort({ createdAt: 1 })
427
+ .toArray();
428
+ return rows.map((row) => ({
429
+ runId: row.runId,
430
+ scorerName: row.scorerName,
431
+ score: row.score,
432
+ reason: row.reason ?? undefined,
433
+ metadata: row.metadata ?? undefined,
434
+ createdAt: row.createdAt,
435
+ }));
406
436
  }
407
437
  async close() { }
408
438
  }
@@ -85,6 +85,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
85
85
  return {
86
86
  stepId,
87
87
  status: 'pending',
88
+ rpcName,
88
89
  result: undefined,
89
90
  error: undefined,
90
91
  attemptCount: 1,
@@ -109,6 +110,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
109
110
  return {
110
111
  stepId: row._id,
111
112
  status: row.status,
113
+ rpcName: row.rpcName ?? null,
112
114
  result: row.result ?? undefined,
113
115
  error: row.error ?? undefined,
114
116
  attemptCount,
@@ -238,6 +240,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
238
240
  return {
239
241
  stepId: row._id,
240
242
  status: row.status,
243
+ rpcName: row.rpcName ?? null,
241
244
  result: row.result ?? undefined,
242
245
  error: row.error ?? undefined,
243
246
  attemptCount,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pikku/mongodb",
3
- "version": "0.12.14",
3
+ "version": "0.12.17",
4
4
  "description": "MongoDB services for Pikku — workflow state, agent runs, secrets and channel stores",
5
5
  "author": "yasser.fadl@gmail.com",
6
6
  "license": "MIT",
@@ -18,7 +18,7 @@
18
18
  "prepublishOnly": "yarn build"
19
19
  },
20
20
  "peerDependencies": {
21
- "@pikku/core": "^0.12.76"
21
+ "@pikku/core": "^0.12.82"
22
22
  },
23
23
  "dependencies": {
24
24
  "mongodb": "^6.12.0"
package/src/index.ts CHANGED
@@ -8,7 +8,6 @@ export { MongoDBDeploymentService } from './mongodb-deployment-service.js'
8
8
  export { MongoDBAIStorageService } from './mongodb-ai-storage-service.js'
9
9
  export { MongoDBAgentRunService } from './mongodb-agent-run-service.js'
10
10
  export { MongoDBSecretService } from './mongodb-secret-service.js'
11
- export type { MongoDBSecretServiceConfig } from './mongodb-secret-service.js'
12
11
 
13
12
  export type { WorkflowRunService } from '@pikku/core/workflow'
14
13
  export type { AgentRunService, AgentRunRow } from '@pikku/core/ai-agent'
@@ -2,8 +2,10 @@ import type {
2
2
  AIStorageService,
3
3
  AIRunStateService,
4
4
  CreateRunInput,
5
+ SaveScoreInput,
5
6
  } from '@pikku/core/services'
6
7
  import type { AIThread, AIMessage, AgentRunState } from '@pikku/core/ai-agent'
8
+ import type { AIRunScore } from '@pikku/core/ai-scorer'
7
9
  import type { Db, Collection } from 'mongodb'
8
10
 
9
11
  interface AIThreadDoc {
@@ -46,6 +48,16 @@ interface AIWorkingMemoryDoc {
46
48
  updatedAt: Date
47
49
  }
48
50
 
51
+ interface AIRunScoreDoc {
52
+ _id: string
53
+ runId: string
54
+ scorerName: string
55
+ score: number
56
+ reason: string | null
57
+ metadata: any | null
58
+ createdAt: Date
59
+ }
60
+
49
61
  interface AIRunDoc {
50
62
  _id: string
51
63
  agentName: string
@@ -71,6 +83,7 @@ export class MongoDBAIStorageService
71
83
  private toolCalls!: Collection<AIToolCallDoc>
72
84
  private workingMemory!: Collection<AIWorkingMemoryDoc>
73
85
  private aiRuns!: Collection<AIRunDoc>
86
+ private aiRunScores!: Collection<AIRunScoreDoc>
74
87
 
75
88
  constructor(private db: Db) {}
76
89
 
@@ -83,6 +96,7 @@ export class MongoDBAIStorageService
83
96
  this.workingMemory =
84
97
  this.db.collection<AIWorkingMemoryDoc>('ai_working_memory')
85
98
  this.aiRuns = this.db.collection<AIRunDoc>('ai_run')
99
+ this.aiRunScores = this.db.collection<AIRunScoreDoc>('ai_run_score')
86
100
 
87
101
  await this.threads.createIndex({ resourceId: 1 })
88
102
 
@@ -95,6 +109,8 @@ export class MongoDBAIStorageService
95
109
 
96
110
  await this.aiRuns.createIndex({ threadId: 1, createdAt: 1 })
97
111
 
112
+ await this.aiRunScores.createIndex({ runId: 1, createdAt: 1 })
113
+
98
114
  this.initialized = true
99
115
  }
100
116
 
@@ -565,11 +581,40 @@ export class MongoDBAIStorageService
565
581
  async resolveApproval(
566
582
  toolCallId: string,
567
583
  status: 'approved' | 'denied'
568
- ): Promise<void> {
569
- await this.toolCalls.updateOne(
570
- { _id: toolCallId },
584
+ ): Promise<boolean> {
585
+ // Only the caller that moves the tool call off 'pending' has claimed it.
586
+ const result = await this.toolCalls.updateOne(
587
+ { _id: toolCallId, approvalStatus: 'pending' },
571
588
  { $set: { approvalStatus: status } }
572
589
  )
590
+ return result.modifiedCount > 0
591
+ }
592
+
593
+ async saveScore(score: SaveScoreInput): Promise<void> {
594
+ await this.aiRunScores.insertOne({
595
+ _id: crypto.randomUUID(),
596
+ runId: score.runId,
597
+ scorerName: score.scorerName,
598
+ score: score.score,
599
+ reason: score.reason ?? null,
600
+ metadata: score.metadata ?? null,
601
+ createdAt: new Date(),
602
+ })
603
+ }
604
+
605
+ async getScores(runId: string): Promise<AIRunScore[]> {
606
+ const rows = await this.aiRunScores
607
+ .find({ runId })
608
+ .sort({ createdAt: 1 })
609
+ .toArray()
610
+ return rows.map((row) => ({
611
+ runId: row.runId,
612
+ scorerName: row.scorerName,
613
+ score: row.score,
614
+ reason: row.reason ?? undefined,
615
+ metadata: row.metadata ?? undefined,
616
+ createdAt: row.createdAt,
617
+ }))
573
618
  }
574
619
 
575
620
  public async close(): Promise<void> {}
@@ -203,6 +203,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
203
203
  return {
204
204
  stepId,
205
205
  status: 'pending',
206
+ rpcName,
206
207
  result: undefined,
207
208
  error: undefined,
208
209
  attemptCount: 1,
@@ -233,6 +234,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
233
234
  return {
234
235
  stepId: row._id,
235
236
  status: row.status as StepState['status'],
237
+ rpcName: row.rpcName ?? null,
236
238
  result: row.result ?? undefined,
237
239
  error: row.error ?? undefined,
238
240
  attemptCount,
@@ -426,6 +428,7 @@ export class MongoDBWorkflowService extends PikkuWorkflowService {
426
428
  return {
427
429
  stepId: row._id,
428
430
  status: row.status as StepState['status'],
431
+ rpcName: row.rpcName ?? null,
429
432
  result: row.result ?? undefined,
430
433
  error: row.error ?? undefined,
431
434
  attemptCount,