@pikku/kysely 0.13.13 → 0.13.16
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 +128 -0
- package/dist/src/index.d.ts +8 -11
- package/dist/src/index.js +5 -6
- package/dist/src/kysely-agent-run-scores.d.ts +10 -0
- package/dist/src/kysely-agent-run-scores.js +34 -0
- package/dist/src/kysely-agent-run-service.d.ts +22 -0
- package/dist/src/kysely-agent-run-service.js +190 -0
- package/dist/src/kysely-agent-run-state-service.d.ts +24 -0
- package/dist/src/kysely-agent-run-state-service.js +169 -0
- package/dist/src/kysely-agent-storage-service.d.ts +40 -0
- package/dist/src/kysely-agent-storage-service.js +533 -0
- package/dist/src/kysely-ai-agent-run-service.d.ts +2 -1
- package/dist/src/kysely-ai-run-scores.d.ts +10 -0
- package/dist/src/kysely-ai-run-scores.js +34 -0
- package/dist/src/kysely-ai-run-state-service.d.ts +6 -2
- package/dist/src/kysely-ai-run-state-service.js +7 -0
- package/dist/src/kysely-ai-storage-service.d.ts +4 -1
- package/dist/src/kysely-ai-storage-service.js +7 -0
- package/dist/src/kysely-audit-service.d.ts +3 -1
- package/dist/src/kysely-deployment-service.d.ts +1 -1
- package/dist/src/kysely-deployment-service.js +2 -2
- package/dist/src/kysely-eventhub-store.d.ts +1 -1
- package/dist/src/kysely-eventhub-store.js +1 -1
- package/dist/src/kysely-scope-service.d.ts +4 -3
- package/dist/src/kysely-tables.d.ts +23 -11
- package/dist/src/kysely-virtual-user-run-store.d.ts +1 -1
- package/dist/src/kysely-webhook-service.d.ts +2 -1
- package/dist/src/kysely-webhook-service.js +2 -1
- package/dist/src/kysely-workflow-mirror.d.ts +2 -2
- package/dist/src/kysely-workflow-run-service.d.ts +1 -1
- package/dist/src/kysely-workflow-service.d.ts +3 -2
- package/dist/src/kysely-workflow-service.js +1 -1
- package/dist/src/schema/agent.schema.d.ts +12 -0
- package/dist/src/schema/agent.schema.js +102 -0
- package/dist/src/schema/ai.schema.js +13 -0
- package/dist/src/schema/index.js +4 -4
- package/dist/src/serialize-plugin.d.ts +0 -4
- package/dist/src/serialize-plugin.js +0 -4
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +2 -2
- package/src/index.ts +9 -22
- package/src/kysely-agent-run-scores.ts +47 -0
- package/src/{kysely-ai-agent-run-service.ts → kysely-agent-run-service.ts} +17 -18
- package/src/{kysely-ai-run-state-service.ts → kysely-agent-run-state-service.ts} +27 -12
- package/src/{kysely-ai-storage-service.ts → kysely-agent-storage-service.ts} +61 -40
- package/src/kysely-audit-service.ts +4 -2
- package/src/kysely-deployment-service.ts +3 -3
- package/src/kysely-eventhub-store.ts +1 -1
- package/src/kysely-scope-service.ts +7 -4
- package/src/kysely-services.test.ts +11 -10
- package/src/kysely-tables.ts +24 -11
- package/src/kysely-virtual-user-run-store.ts +2 -2
- package/src/kysely-webhook-service.ts +2 -2
- package/src/kysely-workflow-mirror.ts +2 -2
- package/src/kysely-workflow-run-service.ts +1 -1
- package/src/kysely-workflow-service.ts +3 -12
- package/src/schema/{ai.schema.ts → agent.schema.ts} +46 -25
- package/src/schema/index.ts +4 -4
- package/src/schema/schema.test.ts +6 -6
- package/src/serialize-plugin.ts +0 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,131 @@
|
|
|
1
|
+
## 0.13.16
|
|
2
|
+
|
|
3
|
+
### Patch Changes
|
|
4
|
+
|
|
5
|
+
- 7406bfe: Rename the agent runtime from `AI*` to `Agent*` (#596)
|
|
6
|
+
|
|
7
|
+
`AI` described the model provider, not the thing being named. Every symbol that
|
|
8
|
+
belongs to the agent runtime now says `Agent`; the symbols that genuinely wrap a
|
|
9
|
+
model provider — `AIEmbeddingService`, `AIProviderOptions`, `AIEmbedParams`,
|
|
10
|
+
`AITranscriptionParams`, `AIGenerateImageParams` and their siblings, and the
|
|
11
|
+
`@pikku/ai-vercel` / `@pikku/ai-deepinfra` / `@pikku/ai-voice` packages — keep
|
|
12
|
+
their names.
|
|
13
|
+
|
|
14
|
+
**Wiring**
|
|
15
|
+
- `pikkuAIAgent` → `pikkuAgent`, `pikkuAIScorer` → `pikkuAgentScorer`,
|
|
16
|
+
`pikkuAIJudge` → `pikkuAgentJudge`
|
|
17
|
+
- `CoreAIAgent` → `CoreAgent`, `AIAgentInput` → `AgentInput`, `AIAgentStep` →
|
|
18
|
+
`AgentStep`, `AIMessage` → `AgentMessage`, and the rest of the agent types
|
|
19
|
+
- `AIAgentRunnerService` → `AgentRunnerService`, `AIStorageService` →
|
|
20
|
+
`AgentStorageService`, `AIRunStateService` → `AgentRunStateService`
|
|
21
|
+
|
|
22
|
+
**Entry points**
|
|
23
|
+
|
|
24
|
+
`@pikku/core/agent` → `@pikku/core/agent`, `@pikku/core/agent-scorer` →
|
|
25
|
+
`@pikku/core/agent-scorer`.
|
|
26
|
+
|
|
27
|
+
**Queues**
|
|
28
|
+
|
|
29
|
+
The scorer queues are now `agent-score-fast` and `agent-score-slow`. Drain the
|
|
30
|
+
old `ai-score-fast` / `ai-score-slow` queues before deploying — jobs still
|
|
31
|
+
sitting on them when the new workers start will never be picked up.
|
|
32
|
+
|
|
33
|
+
**Scaffolds**
|
|
34
|
+
|
|
35
|
+
The agent scaffold pikku wrote for your project — `<scaffold>/agent/agent.gen.ts`
|
|
36
|
+
and its schemas file — imports `@pikku/core/ai-agent`, which no longer exists. A
|
|
37
|
+
scaffold is normally written once and then left alone, so `pikku all` would find
|
|
38
|
+
it present and leave the broken import in place. It now deletes an agent scaffold
|
|
39
|
+
importing either removed entry point and regenerates it in the same run. Anything
|
|
40
|
+
you added to that file goes with it, so move local edits out first.
|
|
41
|
+
|
|
42
|
+
**Database**
|
|
43
|
+
|
|
44
|
+
The agent tables are renamed: `ai_threads`, `ai_message`, `ai_tool_call`,
|
|
45
|
+
`ai_working_memory`, `ai_run` and `ai_run_score` become `agent_threads`,
|
|
46
|
+
`agent_message`, `agent_tool_call`, `agent_working_memory`, `agent_run` and
|
|
47
|
+
`agent_run_score`, along with their indexes and the `ai_working_memory_pk`
|
|
48
|
+
constraint. The same rename applies to the MongoDB collections.
|
|
49
|
+
|
|
50
|
+
`ensurePikkuSchema` creates tables it cannot find, so an existing database will
|
|
51
|
+
get empty `agent_*` tables and leave the old data stranded in `ai_*`. Rename
|
|
52
|
+
them before the first boot on the new version:
|
|
53
|
+
|
|
54
|
+
```sql
|
|
55
|
+
ALTER TABLE ai_threads RENAME TO agent_threads;
|
|
56
|
+
ALTER TABLE ai_message RENAME TO agent_message;
|
|
57
|
+
ALTER TABLE ai_tool_call RENAME TO agent_tool_call;
|
|
58
|
+
ALTER TABLE ai_working_memory RENAME TO agent_working_memory;
|
|
59
|
+
ALTER TABLE ai_run RENAME TO agent_run;
|
|
60
|
+
ALTER TABLE ai_run_score RENAME TO agent_run_score;
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
- 6794681: Publish the ecosystem surface as per-area sub-barrels under `@pikku/core/ecosystem/*`, and point generated code, the CLI, the inspector and the runtime adapters at them.
|
|
64
|
+
|
|
65
|
+
348 names that only generated code, the toolchain or a runtime adapter ever imports now have a second home on `@pikku/core/ecosystem/<area>` — one sub-barrel per area, matching how core already publishes its entrypoints, so no single barrel grows without bound and a consumer only pulls in the area it uses.
|
|
66
|
+
|
|
67
|
+
This step is additive: every name is still exported from the entrypoint it was published from before, so nothing downstream breaks. Removing them from the app-facing barrels is a later change, and needs a release carrying `./ecosystem/*` first.
|
|
68
|
+
|
|
69
|
+
- a7fcd2e: Declare dependencies that were imported but missing from `package.json`
|
|
70
|
+
|
|
71
|
+
`@pikku/openapi-parser` and `@pikku/better-auth` imported `zod`, `@pikku/next`
|
|
72
|
+
imported `path-to-regexp`, `@pikku/cli` imported `kysely`, and
|
|
73
|
+
`@pikku/assistant-ui` imported `rxjs`, none of which were declared. Each
|
|
74
|
+
resolved through Yarn hoisting inside the monorepo and would fail for anyone
|
|
75
|
+
installing the package on its own.
|
|
76
|
+
|
|
77
|
+
`rxjs`, `kysely` and `path-to-regexp` reach consumers through public
|
|
78
|
+
signatures — `Observable<BaseEvent>` is the return type of a published method,
|
|
79
|
+
and `createCoercionPlugin` returns a `KyselyPlugin` — so they are runtime
|
|
80
|
+
dependencies rather than build-only ones.
|
|
81
|
+
|
|
82
|
+
`@pikku/assistant-ui` pins `rxjs` to the exact `7.8.1` that `@ag-ui/client`
|
|
83
|
+
pins, rather than a caret range. The two packages exchange `Observable`s, so a
|
|
84
|
+
range that floats to a second copy gives them two incompatible `Observable`
|
|
85
|
+
types.
|
|
86
|
+
|
|
87
|
+
`@pikku/kysely` also drops `SqliteSerializePlugin`, an alias of
|
|
88
|
+
`SerializePlugin` that has been marked `@deprecated` in favour of it. Use
|
|
89
|
+
`SerializePlugin`.
|
|
90
|
+
|
|
91
|
+
- Updated dependencies [7406bfe]
|
|
92
|
+
- Updated dependencies [6794681]
|
|
93
|
+
- @pikku/core@0.12.84
|
|
94
|
+
|
|
95
|
+
## 0.13.15
|
|
96
|
+
|
|
97
|
+
### Patch Changes
|
|
98
|
+
|
|
99
|
+
- 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.
|
|
100
|
+
- Updated dependencies [063f43a]
|
|
101
|
+
- Updated dependencies [ce66bf8]
|
|
102
|
+
- Updated dependencies [d0307a8]
|
|
103
|
+
- Updated dependencies [ce66bf8]
|
|
104
|
+
- Updated dependencies [3ad2131]
|
|
105
|
+
- Updated dependencies [b930dca]
|
|
106
|
+
- Updated dependencies [b95e77d]
|
|
107
|
+
- Updated dependencies [fd9d834]
|
|
108
|
+
- Updated dependencies [8978fbd]
|
|
109
|
+
- @pikku/core@0.12.82
|
|
110
|
+
|
|
111
|
+
## 0.13.14
|
|
112
|
+
|
|
113
|
+
### Patch Changes
|
|
114
|
+
|
|
115
|
+
- e110c55: Add runtime scoring for AI agents: `pikkuAIScorer` for heuristic grades and
|
|
116
|
+
`pikkuAIJudge` for LLM-judged ones, graded off the request path on two queue
|
|
117
|
+
lanes so a slow judge cannot starve the cheap checks. Grades are sampled
|
|
118
|
+
deterministically per `(run, scorer)` and persisted to `ai_run_score`.
|
|
119
|
+
- Updated dependencies [e110c55]
|
|
120
|
+
- Updated dependencies [e110c55]
|
|
121
|
+
- Updated dependencies [e110c55]
|
|
122
|
+
- Updated dependencies [acc8077]
|
|
123
|
+
- Updated dependencies [905f737]
|
|
124
|
+
- Updated dependencies [3cc6428]
|
|
125
|
+
- Updated dependencies [c524adf]
|
|
126
|
+
- Updated dependencies [e110c55]
|
|
127
|
+
- @pikku/core@0.12.81
|
|
128
|
+
|
|
1
129
|
## 0.13.13
|
|
2
130
|
|
|
3
131
|
### Patch Changes
|
package/dist/src/index.d.ts
CHANGED
|
@@ -4,23 +4,20 @@ export { KyselyWorkflowService } from './kysely-workflow-service.js';
|
|
|
4
4
|
export { KyselyWorkflowRunService } from './kysely-workflow-run-service.js';
|
|
5
5
|
export { KyselyWorkflowMirror } from './kysely-workflow-mirror.js';
|
|
6
6
|
export { KyselyDeploymentService } from './kysely-deployment-service.js';
|
|
7
|
-
export {
|
|
8
|
-
export { KyselyAgentRunService } from './kysely-
|
|
9
|
-
export {
|
|
7
|
+
export { KyselyAgentStorageService } from './kysely-agent-storage-service.js';
|
|
8
|
+
export { KyselyAgentRunService } from './kysely-agent-run-service.js';
|
|
9
|
+
export { KyselyAgentRunStateService } from './kysely-agent-run-state-service.js';
|
|
10
10
|
export { KyselySecretService } from './kysely-secret-service.js';
|
|
11
|
-
export type { KyselySecretServiceConfig } from './kysely-secret-service.js';
|
|
12
11
|
export { KyselyCredentialService } from './kysely-credential-service.js';
|
|
13
|
-
export type { KyselyCredentialServiceConfig } from './kysely-credential-service.js';
|
|
14
12
|
export { KyselySessionStore } from './kysely-session-store.js';
|
|
15
13
|
export { KyselyScopeService } from './kysely-scope-service.js';
|
|
16
14
|
export { KyselyWebhookService } from './kysely-webhook-service.js';
|
|
17
15
|
export { createAuditedKysely, type CreateAuditedKyselyOptions, } from './create-audited-kysely.js';
|
|
18
16
|
export { KyselyAuditService } from './kysely-audit-service.js';
|
|
19
17
|
export { KyselyVirtualUserRunStore } from './kysely-virtual-user-run-store.js';
|
|
20
|
-
export { SerializePlugin,
|
|
21
|
-
export {
|
|
22
|
-
export { auditSchema, pikkuSchemas, applyPikkuSchemas, compilePikkuSchemas, ensurePikkuSchema, type EnsureOutcome, resolveRequirements, unsatisfiedRequirements, rawStatement, requiredType, type PikkuSchema, type RequiredTypes, type SchemaRequirement, type UnmetRequirement, type SchemaStatement, type SchemaStatementFactory, } from './schema/index.js';
|
|
18
|
+
export { SerializePlugin, BaseSerializePlugin, type Serializer, type Deserializer, } from './serialize-plugin.js';
|
|
19
|
+
export { auditSchema, pikkuSchemas, applyPikkuSchemas, compilePikkuSchemas, ensurePikkuSchema, type EnsureOutcome, resolveRequirements, type PikkuSchema, type RequiredTypes, type UnmetRequirement, type SchemaStatementFactory, } from './schema/index.js';
|
|
23
20
|
export type { KyselyPikkuDB } from './kysely-tables.js';
|
|
24
|
-
export type { WorkflowRunService } from '@pikku/core/workflow';
|
|
25
|
-
export type { AgentRunService, AgentRunRow } from '@pikku/core/
|
|
26
|
-
export type { VirtualUserRunStore, VirtualUserRunRecord, } from '@pikku/core/virtual-user';
|
|
21
|
+
export type { WorkflowRunService } from '@pikku/core/ecosystem/workflow';
|
|
22
|
+
export type { AgentRunService, AgentRunRow, } from '@pikku/core/ecosystem/agent';
|
|
23
|
+
export type { VirtualUserRunStore, VirtualUserRunRecord, } from '@pikku/core/ecosystem/virtual-user';
|
package/dist/src/index.js
CHANGED
|
@@ -4,9 +4,9 @@ export { KyselyWorkflowService } from './kysely-workflow-service.js';
|
|
|
4
4
|
export { KyselyWorkflowRunService } from './kysely-workflow-run-service.js';
|
|
5
5
|
export { KyselyWorkflowMirror } from './kysely-workflow-mirror.js';
|
|
6
6
|
export { KyselyDeploymentService } from './kysely-deployment-service.js';
|
|
7
|
-
export {
|
|
8
|
-
export { KyselyAgentRunService } from './kysely-
|
|
9
|
-
export {
|
|
7
|
+
export { KyselyAgentStorageService } from './kysely-agent-storage-service.js';
|
|
8
|
+
export { KyselyAgentRunService } from './kysely-agent-run-service.js';
|
|
9
|
+
export { KyselyAgentRunStateService } from './kysely-agent-run-state-service.js';
|
|
10
10
|
export { KyselySecretService } from './kysely-secret-service.js';
|
|
11
11
|
export { KyselyCredentialService } from './kysely-credential-service.js';
|
|
12
12
|
export { KyselySessionStore } from './kysely-session-store.js';
|
|
@@ -15,6 +15,5 @@ export { KyselyWebhookService } from './kysely-webhook-service.js';
|
|
|
15
15
|
export { createAuditedKysely, } from './create-audited-kysely.js';
|
|
16
16
|
export { KyselyAuditService } from './kysely-audit-service.js';
|
|
17
17
|
export { KyselyVirtualUserRunStore } from './kysely-virtual-user-run-store.js';
|
|
18
|
-
export { SerializePlugin,
|
|
19
|
-
export {
|
|
20
|
-
export { auditSchema, pikkuSchemas, applyPikkuSchemas, compilePikkuSchemas, ensurePikkuSchema, resolveRequirements, unsatisfiedRequirements, rawStatement, requiredType, } from './schema/index.js';
|
|
18
|
+
export { SerializePlugin, BaseSerializePlugin, } from './serialize-plugin.js';
|
|
19
|
+
export { auditSchema, pikkuSchemas, applyPikkuSchemas, compilePikkuSchemas, ensurePikkuSchema, resolveRequirements, } from './schema/index.js';
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Kysely } from 'kysely';
|
|
2
|
+
import type { KyselyPikkuDB } from './kysely-tables.js';
|
|
3
|
+
import type { SaveScoreInput } from '@pikku/core/services';
|
|
4
|
+
import type { AgentRunScore } from '@pikku/core/agent-scorer';
|
|
5
|
+
/**
|
|
6
|
+
* Shared by the two Kysely services that implement `AgentRunStateService` — they
|
|
7
|
+
* differ in how they store approvals, not in how they store grades.
|
|
8
|
+
*/
|
|
9
|
+
export declare const saveRunScore: (db: Kysely<KyselyPikkuDB>, score: SaveScoreInput) => Promise<void>;
|
|
10
|
+
export declare const getRunScores: (db: Kysely<KyselyPikkuDB>, runId: string) => Promise<AgentRunScore[]>;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { parseJson } from './kysely-json.js';
|
|
2
|
+
/**
|
|
3
|
+
* Shared by the two Kysely services that implement `AgentRunStateService` — they
|
|
4
|
+
* differ in how they store approvals, not in how they store grades.
|
|
5
|
+
*/
|
|
6
|
+
export const saveRunScore = async (db, score) => {
|
|
7
|
+
await db
|
|
8
|
+
.insertInto('agentRunScore')
|
|
9
|
+
.values({
|
|
10
|
+
id: crypto.randomUUID(),
|
|
11
|
+
runId: score.runId,
|
|
12
|
+
scorerName: score.scorerName,
|
|
13
|
+
score: score.score,
|
|
14
|
+
reason: score.reason ?? null,
|
|
15
|
+
metadata: score.metadata ? JSON.stringify(score.metadata) : null,
|
|
16
|
+
})
|
|
17
|
+
.execute();
|
|
18
|
+
};
|
|
19
|
+
export const getRunScores = async (db, runId) => {
|
|
20
|
+
const rows = await db
|
|
21
|
+
.selectFrom('agentRunScore')
|
|
22
|
+
.selectAll()
|
|
23
|
+
.where('runId', '=', runId)
|
|
24
|
+
.orderBy('createdAt', 'asc')
|
|
25
|
+
.execute();
|
|
26
|
+
return rows.map((row) => ({
|
|
27
|
+
runId: row.runId,
|
|
28
|
+
scorerName: row.scorerName,
|
|
29
|
+
score: Number(row.score),
|
|
30
|
+
reason: row.reason ?? undefined,
|
|
31
|
+
metadata: parseJson(row.metadata),
|
|
32
|
+
createdAt: new Date(row.createdAt),
|
|
33
|
+
}));
|
|
34
|
+
};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { AgentThread, AgentMessage } from '@pikku/core/agent';
|
|
2
|
+
import type { AgentRunRow, AgentRunService } from '@pikku/core/ecosystem/agent';
|
|
3
|
+
import type { Kysely } from 'kysely';
|
|
4
|
+
import type { KyselyPikkuDB } from './kysely-tables.js';
|
|
5
|
+
export declare class KyselyAgentRunService implements AgentRunService {
|
|
6
|
+
private db;
|
|
7
|
+
constructor(db: Kysely<KyselyPikkuDB>);
|
|
8
|
+
listThreads(options?: {
|
|
9
|
+
agentName?: string;
|
|
10
|
+
resourceId?: string;
|
|
11
|
+
owners?: string[];
|
|
12
|
+
limit?: number;
|
|
13
|
+
offset?: number;
|
|
14
|
+
}): Promise<AgentThread[]>;
|
|
15
|
+
getThread(threadId: string): Promise<AgentThread | null>;
|
|
16
|
+
getThreadMessages(threadId: string): Promise<AgentMessage[]>;
|
|
17
|
+
getThreadRuns(threadId: string): Promise<AgentRunRow[]>;
|
|
18
|
+
deleteThread(threadId: string): Promise<boolean>;
|
|
19
|
+
getDistinctAgentNames(): Promise<string[]>;
|
|
20
|
+
private mapThreadRow;
|
|
21
|
+
private mapRunRow;
|
|
22
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { sql } from 'kysely';
|
|
2
|
+
import { parseJson } from './kysely-json.js';
|
|
3
|
+
// Owner ids routinely contain `_` (e.g. `user_123`), which LIKE treats as a
|
|
4
|
+
// single-character wildcard — so a raw prefix would match a foreign owner.
|
|
5
|
+
const escapeLikePattern = (value) => value.replace(/[\\%_]/g, (char) => `\\${char}`);
|
|
6
|
+
export class KyselyAgentRunService {
|
|
7
|
+
db;
|
|
8
|
+
constructor(db) {
|
|
9
|
+
this.db = db;
|
|
10
|
+
}
|
|
11
|
+
async listThreads(options) {
|
|
12
|
+
const { agentName, resourceId, owners, limit = 50, offset = 0, } = options ?? {};
|
|
13
|
+
// An owners constraint is an authorization boundary, so an empty list must
|
|
14
|
+
// match nothing rather than degrade to "no filter".
|
|
15
|
+
if (owners && owners.length === 0)
|
|
16
|
+
return [];
|
|
17
|
+
let query = this.db
|
|
18
|
+
.selectFrom('agentThreads as t')
|
|
19
|
+
.select([
|
|
20
|
+
't.id',
|
|
21
|
+
't.resourceId',
|
|
22
|
+
't.title',
|
|
23
|
+
't.metadata',
|
|
24
|
+
't.createdAt',
|
|
25
|
+
't.updatedAt',
|
|
26
|
+
]);
|
|
27
|
+
if (agentName) {
|
|
28
|
+
query = query.where('t.id', 'in', this.db
|
|
29
|
+
.selectFrom('agentRun')
|
|
30
|
+
.select('threadId')
|
|
31
|
+
.where('agentName', '=', agentName)
|
|
32
|
+
.distinct());
|
|
33
|
+
}
|
|
34
|
+
if (resourceId) {
|
|
35
|
+
query = query.where('t.resourceId', '=', resourceId);
|
|
36
|
+
}
|
|
37
|
+
if (owners) {
|
|
38
|
+
query = query.where((eb) => eb.or(owners.flatMap((owner) => [
|
|
39
|
+
eb('t.resourceId', '=', owner),
|
|
40
|
+
eb('t.resourceId', 'like', sql `${`${escapeLikePattern(owner)}:%`} escape '\\'`),
|
|
41
|
+
])));
|
|
42
|
+
}
|
|
43
|
+
const result = await query
|
|
44
|
+
.orderBy('t.updatedAt', 'desc')
|
|
45
|
+
.limit(limit)
|
|
46
|
+
.offset(offset)
|
|
47
|
+
.execute();
|
|
48
|
+
return result.map((row) => this.mapThreadRow(row));
|
|
49
|
+
}
|
|
50
|
+
async getThread(threadId) {
|
|
51
|
+
const row = await this.db
|
|
52
|
+
.selectFrom('agentThreads')
|
|
53
|
+
.select([
|
|
54
|
+
'id',
|
|
55
|
+
'resourceId',
|
|
56
|
+
'title',
|
|
57
|
+
'metadata',
|
|
58
|
+
'createdAt',
|
|
59
|
+
'updatedAt',
|
|
60
|
+
])
|
|
61
|
+
.where('id', '=', threadId)
|
|
62
|
+
.executeTakeFirst();
|
|
63
|
+
if (!row)
|
|
64
|
+
return null;
|
|
65
|
+
return this.mapThreadRow(row);
|
|
66
|
+
}
|
|
67
|
+
async getThreadMessages(threadId) {
|
|
68
|
+
const [msgResult, tcResult] = await Promise.all([
|
|
69
|
+
this.db
|
|
70
|
+
.selectFrom('agentMessage')
|
|
71
|
+
.select(['id', 'role', 'content', 'createdAt'])
|
|
72
|
+
.where('threadId', '=', threadId)
|
|
73
|
+
.orderBy('createdAt', 'asc')
|
|
74
|
+
.execute(),
|
|
75
|
+
this.db
|
|
76
|
+
.selectFrom('agentToolCall')
|
|
77
|
+
.select(['id', 'messageId', 'toolName', 'args', 'result'])
|
|
78
|
+
.where('threadId', '=', threadId)
|
|
79
|
+
.orderBy('createdAt', 'asc')
|
|
80
|
+
.execute(),
|
|
81
|
+
]);
|
|
82
|
+
const tcByMessage = new Map();
|
|
83
|
+
for (const tc of tcResult) {
|
|
84
|
+
const msgId = tc.messageId;
|
|
85
|
+
if (!tcByMessage.has(msgId))
|
|
86
|
+
tcByMessage.set(msgId, []);
|
|
87
|
+
tcByMessage.get(msgId).push(tc);
|
|
88
|
+
}
|
|
89
|
+
const messages = [];
|
|
90
|
+
for (const row of msgResult) {
|
|
91
|
+
const msg = {
|
|
92
|
+
id: row.id,
|
|
93
|
+
role: row.role,
|
|
94
|
+
content: row.content ?? undefined,
|
|
95
|
+
createdAt: new Date(row.createdAt),
|
|
96
|
+
};
|
|
97
|
+
const tcs = tcByMessage.get(msg.id);
|
|
98
|
+
if (tcs?.length) {
|
|
99
|
+
msg.toolCalls = tcs.map((tc) => ({
|
|
100
|
+
id: tc.id,
|
|
101
|
+
name: tc.toolName,
|
|
102
|
+
args: parseJson(tc.args),
|
|
103
|
+
}));
|
|
104
|
+
const completed = tcs.filter((tc) => tc.result != null);
|
|
105
|
+
if (completed.length) {
|
|
106
|
+
messages.push(msg);
|
|
107
|
+
messages.push({
|
|
108
|
+
id: `tool-results-${msg.id}`,
|
|
109
|
+
role: 'tool',
|
|
110
|
+
toolResults: completed.map((tc) => ({
|
|
111
|
+
id: tc.id,
|
|
112
|
+
name: tc.toolName,
|
|
113
|
+
result: tc.result,
|
|
114
|
+
})),
|
|
115
|
+
createdAt: msg.createdAt,
|
|
116
|
+
});
|
|
117
|
+
continue;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
messages.push(msg);
|
|
121
|
+
}
|
|
122
|
+
return messages;
|
|
123
|
+
}
|
|
124
|
+
async getThreadRuns(threadId) {
|
|
125
|
+
const result = await this.db
|
|
126
|
+
.selectFrom('agentRun')
|
|
127
|
+
.select([
|
|
128
|
+
'runId',
|
|
129
|
+
'agentName',
|
|
130
|
+
'threadId',
|
|
131
|
+
'resourceId',
|
|
132
|
+
'status',
|
|
133
|
+
'errorMessage',
|
|
134
|
+
'suspendReason',
|
|
135
|
+
'missingRpcs',
|
|
136
|
+
'usageInputTokens',
|
|
137
|
+
'usageOutputTokens',
|
|
138
|
+
'usageModel',
|
|
139
|
+
'createdAt',
|
|
140
|
+
'updatedAt',
|
|
141
|
+
])
|
|
142
|
+
.where('threadId', '=', threadId)
|
|
143
|
+
.orderBy('createdAt', 'desc')
|
|
144
|
+
.execute();
|
|
145
|
+
return result.map((row) => this.mapRunRow(row));
|
|
146
|
+
}
|
|
147
|
+
async deleteThread(threadId) {
|
|
148
|
+
const result = await this.db
|
|
149
|
+
.deleteFrom('agentThreads')
|
|
150
|
+
.where('id', '=', threadId)
|
|
151
|
+
.executeTakeFirst();
|
|
152
|
+
return BigInt(result.numDeletedRows) > 0n;
|
|
153
|
+
}
|
|
154
|
+
async getDistinctAgentNames() {
|
|
155
|
+
const result = await this.db
|
|
156
|
+
.selectFrom('agentRun')
|
|
157
|
+
.select('agentName')
|
|
158
|
+
.distinct()
|
|
159
|
+
.orderBy('agentName')
|
|
160
|
+
.execute();
|
|
161
|
+
return result.map((row) => row.agentName);
|
|
162
|
+
}
|
|
163
|
+
mapThreadRow(row) {
|
|
164
|
+
return {
|
|
165
|
+
id: row.id,
|
|
166
|
+
resourceId: row.resourceId,
|
|
167
|
+
title: row.title ?? undefined,
|
|
168
|
+
metadata: parseJson(row.metadata),
|
|
169
|
+
createdAt: new Date(row.createdAt),
|
|
170
|
+
updatedAt: new Date(row.updatedAt),
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
mapRunRow(row) {
|
|
174
|
+
return {
|
|
175
|
+
runId: row.runId,
|
|
176
|
+
agentName: row.agentName,
|
|
177
|
+
threadId: row.threadId,
|
|
178
|
+
resourceId: row.resourceId,
|
|
179
|
+
status: row.status,
|
|
180
|
+
errorMessage: row.errorMessage ?? undefined,
|
|
181
|
+
suspendReason: row.suspendReason ?? undefined,
|
|
182
|
+
missingRpcs: parseJson(row.missingRpcs),
|
|
183
|
+
usageInputTokens: Number(row.usageInputTokens),
|
|
184
|
+
usageOutputTokens: Number(row.usageOutputTokens),
|
|
185
|
+
usageModel: row.usageModel,
|
|
186
|
+
createdAt: new Date(row.createdAt),
|
|
187
|
+
updatedAt: new Date(row.updatedAt),
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import type { Kysely } from 'kysely';
|
|
2
|
+
import type { KyselyPikkuDB } from './kysely-tables.js';
|
|
3
|
+
import type { AgentRunStateService, CreateRunInput, SaveScoreInput } from '@pikku/core/services';
|
|
4
|
+
import type { AgentRunState } from '@pikku/core/agent';
|
|
5
|
+
import type { PendingApproval } from '@pikku/core/ecosystem/agent';
|
|
6
|
+
import type { AgentRunScore } from '@pikku/core/agent-scorer';
|
|
7
|
+
export declare class KyselyAgentRunStateService implements AgentRunStateService {
|
|
8
|
+
private db;
|
|
9
|
+
private initialized;
|
|
10
|
+
constructor(db: Kysely<KyselyPikkuDB>);
|
|
11
|
+
init(): Promise<void>;
|
|
12
|
+
createRun(run: CreateRunInput): Promise<string>;
|
|
13
|
+
updateRun(runId: string, updates: Partial<AgentRunState>): Promise<void>;
|
|
14
|
+
getRun(runId: string): Promise<AgentRunState | null>;
|
|
15
|
+
getRunsByThread(threadId: string): Promise<AgentRunState[]>;
|
|
16
|
+
resolveApproval(toolCallId: string, status: 'approved' | 'denied'): Promise<boolean>;
|
|
17
|
+
findRunByToolCallId(toolCallId: string): Promise<{
|
|
18
|
+
run: AgentRunState;
|
|
19
|
+
approval: PendingApproval;
|
|
20
|
+
} | null>;
|
|
21
|
+
saveScore(score: SaveScoreInput): Promise<void>;
|
|
22
|
+
getScores(runId: string): Promise<AgentRunScore[]>;
|
|
23
|
+
private toRunState;
|
|
24
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
import { sql } from 'kysely';
|
|
2
|
+
import { ensurePikkuSchema } from './schema/index.js';
|
|
3
|
+
import { agentSchema } from './schema/agent.schema.js';
|
|
4
|
+
import { getRunScores, saveRunScore } from './kysely-agent-run-scores.js';
|
|
5
|
+
export class KyselyAgentRunStateService {
|
|
6
|
+
db;
|
|
7
|
+
initialized = false;
|
|
8
|
+
constructor(db) {
|
|
9
|
+
this.db = db;
|
|
10
|
+
}
|
|
11
|
+
async init() {
|
|
12
|
+
if (this.initialized)
|
|
13
|
+
return;
|
|
14
|
+
await ensurePikkuSchema(this.db, agentSchema);
|
|
15
|
+
this.initialized = true;
|
|
16
|
+
}
|
|
17
|
+
async createRun(run) {
|
|
18
|
+
const runId = `run-${crypto.randomUUID()}`;
|
|
19
|
+
await this.db
|
|
20
|
+
.insertInto('agentRun')
|
|
21
|
+
.values({
|
|
22
|
+
runId,
|
|
23
|
+
agentName: run.agentName,
|
|
24
|
+
threadId: run.threadId,
|
|
25
|
+
resourceId: run.resourceId,
|
|
26
|
+
status: run.status ?? 'running',
|
|
27
|
+
errorMessage: run.errorMessage ?? null,
|
|
28
|
+
suspendReason: run.suspendReason ?? null,
|
|
29
|
+
missingRpcs: run.missingRpcs ? JSON.stringify(run.missingRpcs) : null,
|
|
30
|
+
usageInputTokens: run.usage?.inputTokens ?? 0,
|
|
31
|
+
usageOutputTokens: run.usage?.outputTokens ?? 0,
|
|
32
|
+
usageModel: run.usage?.model ?? '',
|
|
33
|
+
})
|
|
34
|
+
.execute();
|
|
35
|
+
return runId;
|
|
36
|
+
}
|
|
37
|
+
async updateRun(runId, updates) {
|
|
38
|
+
const values = {
|
|
39
|
+
updatedAt: sql `CURRENT_TIMESTAMP`,
|
|
40
|
+
};
|
|
41
|
+
if (updates.status !== undefined)
|
|
42
|
+
values.status = updates.status;
|
|
43
|
+
if (updates.errorMessage !== undefined)
|
|
44
|
+
values.errorMessage = updates.errorMessage;
|
|
45
|
+
if (updates.suspendReason !== undefined)
|
|
46
|
+
values.suspendReason = updates.suspendReason;
|
|
47
|
+
if (updates.missingRpcs !== undefined)
|
|
48
|
+
values.missingRpcs = JSON.stringify(updates.missingRpcs);
|
|
49
|
+
if (updates.pendingApprovals !== undefined)
|
|
50
|
+
values.pendingApprovals = JSON.stringify(updates.pendingApprovals);
|
|
51
|
+
if (updates.usage) {
|
|
52
|
+
values.usageInputTokens = updates.usage.inputTokens;
|
|
53
|
+
values.usageOutputTokens = updates.usage.outputTokens;
|
|
54
|
+
values.usageModel = updates.usage.model;
|
|
55
|
+
}
|
|
56
|
+
await this.db
|
|
57
|
+
.updateTable('agentRun')
|
|
58
|
+
.set(values)
|
|
59
|
+
.where('runId', '=', runId)
|
|
60
|
+
.execute();
|
|
61
|
+
}
|
|
62
|
+
async getRun(runId) {
|
|
63
|
+
const row = await this.db
|
|
64
|
+
.selectFrom('agentRun')
|
|
65
|
+
.selectAll()
|
|
66
|
+
.where('runId', '=', runId)
|
|
67
|
+
.executeTakeFirst();
|
|
68
|
+
return row ? this.toRunState(row) : null;
|
|
69
|
+
}
|
|
70
|
+
async getRunsByThread(threadId) {
|
|
71
|
+
const rows = await this.db
|
|
72
|
+
.selectFrom('agentRun')
|
|
73
|
+
.selectAll()
|
|
74
|
+
.where('threadId', '=', threadId)
|
|
75
|
+
.orderBy('createdAt', 'desc')
|
|
76
|
+
.execute();
|
|
77
|
+
return rows.map((r) => this.toRunState(r));
|
|
78
|
+
}
|
|
79
|
+
async resolveApproval(toolCallId, status) {
|
|
80
|
+
const rows = await this.db
|
|
81
|
+
.selectFrom('agentRun')
|
|
82
|
+
.select(['runId', 'pendingApprovals'])
|
|
83
|
+
.where('status', '=', 'suspended')
|
|
84
|
+
.execute();
|
|
85
|
+
for (const row of rows) {
|
|
86
|
+
let approvals = [];
|
|
87
|
+
if (row.pendingApprovals) {
|
|
88
|
+
try {
|
|
89
|
+
approvals = JSON.parse(row.pendingApprovals);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
console.warn(`Failed to parse pendingApprovals for run ${row.runId}, treating as empty`);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
const filtered = approvals.filter((a) => a.toolCallId !== toolCallId);
|
|
96
|
+
if (filtered.length !== approvals.length) {
|
|
97
|
+
const updates = {
|
|
98
|
+
pendingApprovals: filtered.length > 0 ? JSON.stringify(filtered) : null,
|
|
99
|
+
updatedAt: sql `CURRENT_TIMESTAMP`,
|
|
100
|
+
};
|
|
101
|
+
if (filtered.length === 0) {
|
|
102
|
+
updates.status = status;
|
|
103
|
+
}
|
|
104
|
+
// Compare-and-swap on the list we read: a concurrent approver that got
|
|
105
|
+
// there first has already rewritten it, and this update matches no row.
|
|
106
|
+
const result = await this.db
|
|
107
|
+
.updateTable('agentRun')
|
|
108
|
+
.set(updates)
|
|
109
|
+
.where('runId', '=', row.runId)
|
|
110
|
+
.where('status', '=', 'suspended')
|
|
111
|
+
.where('pendingApprovals', '=', row.pendingApprovals)
|
|
112
|
+
.executeTakeFirst();
|
|
113
|
+
return Number(result?.numUpdatedRows ?? 0) > 0;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
async findRunByToolCallId(toolCallId) {
|
|
119
|
+
const rows = await this.db
|
|
120
|
+
.selectFrom('agentRun')
|
|
121
|
+
.selectAll()
|
|
122
|
+
.where('status', '=', 'suspended')
|
|
123
|
+
.execute();
|
|
124
|
+
for (const row of rows) {
|
|
125
|
+
let approvals = [];
|
|
126
|
+
if (row.pendingApprovals) {
|
|
127
|
+
try {
|
|
128
|
+
approvals = JSON.parse(row.pendingApprovals);
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
console.warn(`Failed to parse pendingApprovals for run ${row.runId}, treating as empty`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
const approval = approvals.find((a) => a.toolCallId === toolCallId);
|
|
135
|
+
if (approval) {
|
|
136
|
+
return { run: this.toRunState(row), approval };
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
async saveScore(score) {
|
|
142
|
+
await saveRunScore(this.db, score);
|
|
143
|
+
}
|
|
144
|
+
async getScores(runId) {
|
|
145
|
+
return getRunScores(this.db, runId);
|
|
146
|
+
}
|
|
147
|
+
toRunState(row) {
|
|
148
|
+
return {
|
|
149
|
+
runId: row.runId,
|
|
150
|
+
agentName: row.agentName,
|
|
151
|
+
threadId: row.threadId,
|
|
152
|
+
resourceId: row.resourceId,
|
|
153
|
+
status: row.status,
|
|
154
|
+
errorMessage: row.errorMessage ?? undefined,
|
|
155
|
+
suspendReason: row.suspendReason ?? undefined,
|
|
156
|
+
missingRpcs: row.missingRpcs ? JSON.parse(row.missingRpcs) : undefined,
|
|
157
|
+
pendingApprovals: row.pendingApprovals
|
|
158
|
+
? JSON.parse(row.pendingApprovals)
|
|
159
|
+
: undefined,
|
|
160
|
+
usage: {
|
|
161
|
+
inputTokens: row.usageInputTokens ?? 0,
|
|
162
|
+
outputTokens: row.usageOutputTokens ?? 0,
|
|
163
|
+
model: row.usageModel ?? '',
|
|
164
|
+
},
|
|
165
|
+
createdAt: new Date(row.createdAt),
|
|
166
|
+
updatedAt: new Date(row.updatedAt),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|