@pikku/core 0.12.1 → 0.12.2
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 +16 -0
- package/dist/middleware-runner.js +1 -0
- package/dist/permissions.js +1 -0
- package/dist/pikku-state.js +4 -0
- package/dist/services/gateway-service.d.ts +19 -0
- package/dist/services/gateway-service.js +1 -0
- package/dist/services/index.d.ts +2 -0
- package/dist/services/index.js +1 -0
- package/dist/services/local-gateway-service.d.ts +22 -0
- package/dist/services/local-gateway-service.js +51 -0
- package/dist/types/core.types.d.ts +3 -1
- package/dist/types/state.types.d.ts +5 -0
- package/dist/utils.js +1 -1
- package/dist/wirings/ai-agent/ai-agent-assistant-ui.js +27 -5
- package/dist/wirings/ai-agent/ai-agent-memory.js +13 -2
- package/dist/wirings/ai-agent/ai-agent-prepare.js +14 -1
- package/dist/wirings/ai-agent/ai-agent-stream.js +17 -2
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +35 -2
- package/dist/wirings/ai-agent/index.d.ts +1 -1
- package/dist/wirings/channel/channel-handler.d.ts +5 -2
- package/dist/wirings/channel/channel-handler.js +11 -1
- package/dist/wirings/channel/channel-middleware-runner.js +1 -0
- package/dist/wirings/channel/channel.types.d.ts +6 -0
- package/dist/wirings/channel/index.d.ts +1 -1
- package/dist/wirings/channel/local/local-channel-handler.d.ts +7 -0
- package/dist/wirings/channel/local/local-channel-handler.js +17 -0
- package/dist/wirings/channel/local/local-channel-runner.js +14 -1
- package/dist/wirings/channel/pikku-abstract-channel-handler.d.ts +2 -1
- package/dist/wirings/channel/pikku-abstract-channel-handler.js +1 -0
- package/dist/wirings/channel/serverless/serverless-channel-runner.js +1 -1
- package/dist/wirings/cli/cli-runner.js +3 -0
- package/dist/wirings/gateway/gateway-runner.d.ts +24 -0
- package/dist/wirings/gateway/gateway-runner.js +325 -0
- package/dist/wirings/gateway/gateway.types.d.ts +127 -0
- package/dist/wirings/gateway/gateway.types.js +1 -0
- package/dist/wirings/gateway/index.d.ts +2 -0
- package/dist/wirings/gateway/index.js +1 -0
- package/dist/wirings/http/http-runner.js +3 -0
- package/package.json +3 -2
- package/src/middleware-runner.ts +1 -0
- package/src/permissions.ts +1 -0
- package/src/pikku-state.ts +5 -0
- package/src/services/gateway-service.ts +20 -0
- package/src/services/index.ts +2 -0
- package/src/services/local-gateway-service.ts +62 -0
- package/src/types/core.types.ts +3 -0
- package/src/types/state.types.ts +8 -0
- package/src/utils.ts +1 -1
- package/src/wirings/ai-agent/ai-agent-assistant-ui.ts +33 -9
- package/src/wirings/ai-agent/ai-agent-memory.ts +10 -1
- package/src/wirings/ai-agent/ai-agent-prepare.ts +19 -1
- package/src/wirings/ai-agent/ai-agent-stream.ts +13 -2
- package/src/wirings/ai-agent/ai-agent.types.ts +34 -2
- package/src/wirings/ai-agent/index.ts +2 -0
- package/src/wirings/channel/channel-handler.ts +30 -2
- package/src/wirings/channel/channel-middleware-runner.ts +1 -0
- package/src/wirings/channel/channel.types.ts +11 -0
- package/src/wirings/channel/index.ts +1 -0
- package/src/wirings/channel/local/local-channel-handler.ts +26 -0
- package/src/wirings/channel/local/local-channel-runner.ts +15 -1
- package/src/wirings/channel/pikku-abstract-channel-handler.test.ts +2 -0
- package/src/wirings/channel/pikku-abstract-channel-handler.ts +8 -1
- package/src/wirings/channel/serverless/serverless-channel-runner.ts +1 -1
- package/src/wirings/cli/cli-runner.ts +3 -0
- package/src/wirings/gateway/gateway-runner.test.ts +474 -0
- package/src/wirings/gateway/gateway-runner.ts +411 -0
- package/src/wirings/gateway/gateway.types.ts +149 -0
- package/src/wirings/gateway/index.ts +13 -0
- package/src/wirings/http/http-runner.ts +3 -0
- package/tsconfig.tsbuildinfo +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
## 0.12.0
|
|
2
2
|
|
|
3
|
+
## 0.12.2
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- cc4c9e9: Add gateway meta-wiring for messaging platforms:
|
|
8
|
+
|
|
9
|
+
- New `wireGateway()` API with three transport types: webhook, websocket, listener
|
|
10
|
+
- `GatewayAdapter` interface for platform-specific parse/send logic
|
|
11
|
+
- `PikkuGateway` wire object (`wire.gateway`) with senderId, platform, and send()
|
|
12
|
+
- `GatewayService` interface and `LocalGatewayService` for listener gateway lifecycle
|
|
13
|
+
- `createListenerMessageHandler()` helper for building listener message callbacks
|
|
14
|
+
- Add `'gateway'` to `PikkuWiringTypes` and `gateway` to `PikkuWire`
|
|
15
|
+
- Add `gateway` state block to `PikkuPackageState`
|
|
16
|
+
|
|
17
|
+
- 3e04565: chore: update dependencies to latest minor/patch versions
|
|
18
|
+
|
|
3
19
|
## 0.12.1
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
package/dist/permissions.js
CHANGED
package/dist/pikku-state.js
CHANGED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Abstract GatewayService interface.
|
|
3
|
+
*
|
|
4
|
+
* Implementations manage listener gateway lifecycle — initializing adapters
|
|
5
|
+
* and delivering messages to handler functions.
|
|
6
|
+
*
|
|
7
|
+
* A `LocalGatewayService` starts all listeners unconditionally;
|
|
8
|
+
* a distributed implementation could check leader election first.
|
|
9
|
+
*/
|
|
10
|
+
export interface GatewayService {
|
|
11
|
+
/**
|
|
12
|
+
* Start all listener gateways
|
|
13
|
+
*/
|
|
14
|
+
start(): Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Stop all listener gateways
|
|
17
|
+
*/
|
|
18
|
+
stop(): Promise<void>;
|
|
19
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
package/dist/services/index.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export { ConsoleLogger } from './logger-console.js';
|
|
|
12
12
|
export { InMemoryWorkflowService } from './in-memory-workflow-service.js';
|
|
13
13
|
export { InMemoryTriggerService } from './in-memory-trigger-service.js';
|
|
14
14
|
export { InMemoryAIRunStateService } from './in-memory-ai-run-state-service.js';
|
|
15
|
+
export { LocalGatewayService } from './local-gateway-service.js';
|
|
15
16
|
export type { ContentService } from './content-service.js';
|
|
16
17
|
export type { JWTService } from './jwt-service.js';
|
|
17
18
|
export type { Logger } from './logger.js';
|
|
@@ -21,6 +22,7 @@ export type { SchemaService } from './schema-service.js';
|
|
|
21
22
|
export type { SessionService } from './user-session-service.js';
|
|
22
23
|
export type { ScheduledTaskSummary, ScheduledTaskInfo, SchedulerService, } from './scheduler-service.js';
|
|
23
24
|
export type { TriggerService } from './trigger-service.js';
|
|
25
|
+
export type { GatewayService } from './gateway-service.js';
|
|
24
26
|
export type { DeploymentService, DeploymentConfig, DeploymentInfo, DeploymentServiceConfig, } from './deployment-service.js';
|
|
25
27
|
export type { AIStorageService } from './ai-storage-service.js';
|
|
26
28
|
export type { AIAgentRunnerParams, AIAgentRunnerResult, AIAgentStepResult, AIAgentRunnerService, } from './ai-agent-runner-service.js';
|
package/dist/services/index.js
CHANGED
|
@@ -12,3 +12,4 @@ export { ConsoleLogger } from './logger-console.js';
|
|
|
12
12
|
export { InMemoryWorkflowService } from './in-memory-workflow-service.js';
|
|
13
13
|
export { InMemoryTriggerService } from './in-memory-trigger-service.js';
|
|
14
14
|
export { InMemoryAIRunStateService } from './in-memory-ai-run-state-service.js';
|
|
15
|
+
export { LocalGatewayService } from './local-gateway-service.js';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { GatewayService } from './gateway-service.js';
|
|
2
|
+
/**
|
|
3
|
+
* Local GatewayService implementation.
|
|
4
|
+
*
|
|
5
|
+
* Starts all registered listener gateways unconditionally (single-process).
|
|
6
|
+
* For distributed deployments, implement `GatewayService` with leader
|
|
7
|
+
* election or similar coordination.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* const gatewayService = new LocalGatewayService()
|
|
12
|
+
* await gatewayService.start()
|
|
13
|
+
*
|
|
14
|
+
* // On shutdown
|
|
15
|
+
* await gatewayService.stop()
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
export declare class LocalGatewayService implements GatewayService {
|
|
19
|
+
private activeAdapters;
|
|
20
|
+
start(): Promise<void>;
|
|
21
|
+
stop(): Promise<void>;
|
|
22
|
+
}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { pikkuState, getSingletonServices } from '../pikku-state.js';
|
|
2
|
+
import { createListenerMessageHandler } from '../wirings/gateway/gateway-runner.js';
|
|
3
|
+
/**
|
|
4
|
+
* Local GatewayService implementation.
|
|
5
|
+
*
|
|
6
|
+
* Starts all registered listener gateways unconditionally (single-process).
|
|
7
|
+
* For distributed deployments, implement `GatewayService` with leader
|
|
8
|
+
* election or similar coordination.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* const gatewayService = new LocalGatewayService()
|
|
13
|
+
* await gatewayService.start()
|
|
14
|
+
*
|
|
15
|
+
* // On shutdown
|
|
16
|
+
* await gatewayService.stop()
|
|
17
|
+
* ```
|
|
18
|
+
*/
|
|
19
|
+
export class LocalGatewayService {
|
|
20
|
+
activeAdapters = new Map();
|
|
21
|
+
async start() {
|
|
22
|
+
const singletonServices = getSingletonServices();
|
|
23
|
+
const gateways = pikkuState(null, 'gateway', 'gateways');
|
|
24
|
+
for (const [name, config] of gateways) {
|
|
25
|
+
if (config.type !== 'listener')
|
|
26
|
+
continue;
|
|
27
|
+
if (this.activeAdapters.has(name))
|
|
28
|
+
continue;
|
|
29
|
+
const handleMessage = createListenerMessageHandler(name, config, singletonServices);
|
|
30
|
+
await config.adapter.init(handleMessage);
|
|
31
|
+
this.activeAdapters.set(name, config.adapter);
|
|
32
|
+
singletonServices.logger.info(`Started listener gateway: ${name}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async stop() {
|
|
36
|
+
for (const [name, adapter] of this.activeAdapters) {
|
|
37
|
+
try {
|
|
38
|
+
await adapter.close();
|
|
39
|
+
}
|
|
40
|
+
catch (e) {
|
|
41
|
+
try {
|
|
42
|
+
getSingletonServices().logger.error(`Error closing listener gateway '${name}':`, e);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// logger may not be available during shutdown
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
this.activeAdapters.clear();
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -13,6 +13,7 @@ import type { PikkuCLI } from '../wirings/cli/cli.types.js';
|
|
|
13
13
|
import type { PikkuWorkflowWire, WorkflowService, WorkflowServiceConfig, WorkflowStepWire } from '../wirings/workflow/workflow.types.js';
|
|
14
14
|
import type { PikkuGraphWire } from '../wirings/workflow/graph/workflow-graph.types.js';
|
|
15
15
|
import type { PikkuTrigger } from '../wirings/trigger/trigger.types.js';
|
|
16
|
+
import type { PikkuGateway } from '../wirings/gateway/gateway.types.js';
|
|
16
17
|
import type { SchedulerService } from '../services/scheduler-service.js';
|
|
17
18
|
import type { DeploymentService } from '../services/deployment-service.js';
|
|
18
19
|
import type { AIStorageService } from '../services/ai-storage-service.js';
|
|
@@ -20,7 +21,7 @@ import type { ContentService } from '../services/content-service.js';
|
|
|
20
21
|
import type { AIAgentRunnerService } from '../services/ai-agent-runner-service.js';
|
|
21
22
|
import type { AIRunStateService } from '../services/ai-run-state-service.js';
|
|
22
23
|
import type { PikkuAIMiddlewareHooks } from '../wirings/ai-agent/ai-agent.types.js';
|
|
23
|
-
export type PikkuWiringTypes = 'http' | 'scheduler' | 'trigger' | 'channel' | 'rpc' | 'queue' | 'mcp' | 'cli' | 'workflow' | 'agent';
|
|
24
|
+
export type PikkuWiringTypes = 'http' | 'scheduler' | 'trigger' | 'channel' | 'rpc' | 'queue' | 'mcp' | 'cli' | 'workflow' | 'agent' | 'gateway';
|
|
24
25
|
export interface FunctionServicesMeta {
|
|
25
26
|
optimized: boolean;
|
|
26
27
|
services: string[];
|
|
@@ -182,6 +183,7 @@ export type PikkuWire<In = unknown, Out = unknown, HasInitialSession extends boo
|
|
|
182
183
|
workflowStep: WorkflowStepWire;
|
|
183
184
|
graph: PikkuGraphWire;
|
|
184
185
|
trigger: PikkuTrigger<TriggerOutput>;
|
|
186
|
+
gateway: PikkuGateway;
|
|
185
187
|
session: HasInitialSession extends true ? UserSession : UserSession | undefined;
|
|
186
188
|
/** Update and persist the current session */
|
|
187
189
|
setSession: (session: CoreUserSession) => Promise<void> | void;
|
|
@@ -6,6 +6,7 @@ import type { CLIMeta, CLIProgramState } from '../wirings/cli/cli.types.js';
|
|
|
6
6
|
import type { HTTPMethod, CoreHTTPFunctionWiring, HTTPWiringsMeta } from '../wirings/http/http.types.js';
|
|
7
7
|
import type { CoreMCPResource, MCPResourceMeta, MCPToolMeta, CoreMCPPrompt, MCPPromptMeta } from '../wirings/mcp/mcp.types.js';
|
|
8
8
|
import type { CoreAIAgent, AIAgentMeta } from '../wirings/ai-agent/ai-agent.types.js';
|
|
9
|
+
import type { CoreGateway, GatewaysMeta } from '../wirings/gateway/gateway.types.js';
|
|
9
10
|
import type { CoreQueueWorker, QueueWorkersMeta } from '../wirings/queue/queue.types.js';
|
|
10
11
|
import type { CoreScheduledTask, ScheduledTasksMeta } from '../wirings/scheduler/scheduler.types.js';
|
|
11
12
|
import type { CoreWorkflow, WorkflowsRuntimeMeta } from '../wirings/workflow/workflow.types.js';
|
|
@@ -74,6 +75,10 @@ export interface PikkuPackageState {
|
|
|
74
75
|
agents: Map<string, CoreAIAgent>;
|
|
75
76
|
agentsMeta: AIAgentMeta;
|
|
76
77
|
};
|
|
78
|
+
gateway: {
|
|
79
|
+
gateways: Map<string, CoreGateway>;
|
|
80
|
+
meta: GatewaysMeta;
|
|
81
|
+
};
|
|
77
82
|
cli: {
|
|
78
83
|
meta: CLIMeta | Record<string, any>;
|
|
79
84
|
programs: Record<string, CLIProgramState>;
|
package/dist/utils.js
CHANGED
|
@@ -82,7 +82,7 @@ const stopService = async (logger, name, service) => {
|
|
|
82
82
|
export const stopSingletonServices = async () => {
|
|
83
83
|
const singletonServices = getSingletonServices();
|
|
84
84
|
const logger = singletonServices.logger;
|
|
85
|
-
//
|
|
85
|
+
// Stop all addon package singleton services
|
|
86
86
|
const stateMap = getAllPackageStates();
|
|
87
87
|
if (stateMap.size > 0) {
|
|
88
88
|
for (const [packageName, packageState] of stateMap) {
|
|
@@ -29,6 +29,7 @@ export function createAssistantUIChannel(parent) {
|
|
|
29
29
|
return parent.state;
|
|
30
30
|
},
|
|
31
31
|
close: () => parent.close(),
|
|
32
|
+
sendBinary: (data) => parent.sendBinary(data),
|
|
32
33
|
send: (event) => {
|
|
33
34
|
switch (event.type) {
|
|
34
35
|
case 'text-delta':
|
|
@@ -39,6 +40,14 @@ export function createAssistantUIChannel(parent) {
|
|
|
39
40
|
ensureStarted();
|
|
40
41
|
sendChunk({ type: 'reasoning-delta', delta: event.text });
|
|
41
42
|
break;
|
|
43
|
+
case 'audio-delta':
|
|
44
|
+
ensureStarted();
|
|
45
|
+
sendChunk({ type: 'audio-delta', data: event });
|
|
46
|
+
break;
|
|
47
|
+
case 'audio-done':
|
|
48
|
+
ensureStarted();
|
|
49
|
+
sendChunk({ type: 'audio-done', data: event });
|
|
50
|
+
break;
|
|
42
51
|
case 'tool-call':
|
|
43
52
|
ensureStarted();
|
|
44
53
|
hasToolCalls = true;
|
|
@@ -126,21 +135,34 @@ export function parseAssistantUIInput(input, defaults) {
|
|
|
126
135
|
throw new Error('No user message found in assistant-ui input');
|
|
127
136
|
}
|
|
128
137
|
let messageText;
|
|
138
|
+
let attachments;
|
|
139
|
+
const extractFromParts = (parts) => {
|
|
140
|
+
const textParts = parts.filter((p) => p.type === 'text');
|
|
141
|
+
messageText = textParts.map((p) => String(p.text)).join(' ') || '';
|
|
142
|
+
const nonTextParts = parts.filter((p) => p.type === 'image' || p.type === 'file');
|
|
143
|
+
if (nonTextParts.length > 0) {
|
|
144
|
+
attachments = nonTextParts.map((p) => ({
|
|
145
|
+
type: p.type,
|
|
146
|
+
data: p.data,
|
|
147
|
+
url: p.url,
|
|
148
|
+
mediaType: p.mediaType,
|
|
149
|
+
filename: p.filename,
|
|
150
|
+
}));
|
|
151
|
+
}
|
|
152
|
+
};
|
|
129
153
|
if (typeof lastUserMessage.content === 'string') {
|
|
130
154
|
messageText = lastUserMessage.content;
|
|
131
155
|
}
|
|
132
156
|
else if (Array.isArray(lastUserMessage.parts)) {
|
|
133
|
-
|
|
134
|
-
messageText = textPart ? String(textPart.text) : '';
|
|
157
|
+
extractFromParts(lastUserMessage.parts);
|
|
135
158
|
}
|
|
136
159
|
else if (Array.isArray(lastUserMessage.content)) {
|
|
137
|
-
|
|
138
|
-
messageText = textPart ? String(textPart.text) : '';
|
|
160
|
+
extractFromParts(lastUserMessage.content);
|
|
139
161
|
}
|
|
140
162
|
else {
|
|
141
163
|
messageText = '';
|
|
142
164
|
}
|
|
143
165
|
const threadId = input.threadId ?? input.id ?? randomUUID();
|
|
144
166
|
const resourceId = defaults?.resourceId ?? 'default';
|
|
145
|
-
return { message: messageText, threadId, resourceId };
|
|
167
|
+
return { message: messageText, threadId, resourceId, attachments };
|
|
146
168
|
}
|
|
@@ -175,8 +175,19 @@ function sanitizeToolMessages(messages) {
|
|
|
175
175
|
}
|
|
176
176
|
function estimateTokens(msg) {
|
|
177
177
|
let chars = 0;
|
|
178
|
-
if (msg.content)
|
|
179
|
-
|
|
178
|
+
if (msg.content) {
|
|
179
|
+
if (typeof msg.content === 'string') {
|
|
180
|
+
chars += msg.content.length;
|
|
181
|
+
}
|
|
182
|
+
else {
|
|
183
|
+
for (const part of msg.content) {
|
|
184
|
+
if (part.type === 'text')
|
|
185
|
+
chars += part.text.length;
|
|
186
|
+
else
|
|
187
|
+
chars += 1000;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
}
|
|
180
191
|
if (msg.toolCalls)
|
|
181
192
|
chars += JSON.stringify(msg.toolCalls).length;
|
|
182
193
|
if (msg.toolResults)
|
|
@@ -73,6 +73,7 @@ export function createScopedChannel(parent, agentName, session) {
|
|
|
73
73
|
return capturedApproval;
|
|
74
74
|
},
|
|
75
75
|
close: () => { },
|
|
76
|
+
sendBinary: (data) => parent.sendBinary(data),
|
|
76
77
|
send: (event) => {
|
|
77
78
|
if (event.type === 'done')
|
|
78
79
|
return;
|
|
@@ -307,10 +308,22 @@ export async function prepareAgentRun(agentName, input, params, agentSessionMap,
|
|
|
307
308
|
});
|
|
308
309
|
}
|
|
309
310
|
const contextMessages = await loadContextMessages(memoryConfig, storage, input, workingMemoryJsonSchema);
|
|
311
|
+
const userContent = input.attachments?.length
|
|
312
|
+
? [
|
|
313
|
+
{ type: 'text', text: input.message },
|
|
314
|
+
...input.attachments.map((a) => ({
|
|
315
|
+
type: a.type,
|
|
316
|
+
data: a.data,
|
|
317
|
+
url: a.url,
|
|
318
|
+
mediaType: a.mediaType,
|
|
319
|
+
...(a.filename ? { filename: a.filename } : {}),
|
|
320
|
+
})),
|
|
321
|
+
]
|
|
322
|
+
: input.message;
|
|
310
323
|
const userMessage = {
|
|
311
324
|
id: randomUUID(),
|
|
312
325
|
role: 'user',
|
|
313
|
-
content:
|
|
326
|
+
content: userContent,
|
|
314
327
|
createdAt: new Date(),
|
|
315
328
|
};
|
|
316
329
|
const allMessages = [...contextMessages, ...messages, userMessage];
|
|
@@ -57,6 +57,7 @@ function createPersistingChannel(parent, storage, threadId) {
|
|
|
57
57
|
},
|
|
58
58
|
flush: flushStep,
|
|
59
59
|
close: () => parent.close(),
|
|
60
|
+
sendBinary: (data) => parent.sendBinary(data),
|
|
60
61
|
send: (event) => {
|
|
61
62
|
if (storage) {
|
|
62
63
|
switch (event.type) {
|
|
@@ -345,8 +346,15 @@ export async function streamAIAgent(agentName, input, channel, params, agentSess
|
|
|
345
346
|
allEvents,
|
|
346
347
|
state,
|
|
347
348
|
});
|
|
348
|
-
if (result
|
|
349
|
+
if (result == null)
|
|
350
|
+
return;
|
|
351
|
+
if (Array.isArray(result)) {
|
|
352
|
+
for (const r of result)
|
|
353
|
+
await next(r);
|
|
354
|
+
}
|
|
355
|
+
else {
|
|
349
356
|
await next(result);
|
|
357
|
+
}
|
|
350
358
|
};
|
|
351
359
|
});
|
|
352
360
|
const agentsMeta = pikkuState(packageName, 'agent', 'agentsMeta');
|
|
@@ -586,8 +594,15 @@ async function continueAfterToolResult(run, agent, packageName, resolvedName, st
|
|
|
586
594
|
allEvents,
|
|
587
595
|
state,
|
|
588
596
|
});
|
|
589
|
-
if (result
|
|
597
|
+
if (result == null)
|
|
598
|
+
return;
|
|
599
|
+
if (Array.isArray(result)) {
|
|
600
|
+
for (const r of result)
|
|
601
|
+
await next(r);
|
|
602
|
+
}
|
|
603
|
+
else {
|
|
590
604
|
await next(result);
|
|
605
|
+
}
|
|
591
606
|
};
|
|
592
607
|
});
|
|
593
608
|
const allChannelMiddleware = combineChannelMiddleware('agent', `stream:${run.agentName}`, {
|
|
@@ -10,6 +10,21 @@ export interface AIThread {
|
|
|
10
10
|
createdAt: Date;
|
|
11
11
|
updatedAt: Date;
|
|
12
12
|
}
|
|
13
|
+
export type AIContentPart = {
|
|
14
|
+
type: 'text';
|
|
15
|
+
text: string;
|
|
16
|
+
} | {
|
|
17
|
+
type: 'image';
|
|
18
|
+
data?: string;
|
|
19
|
+
url?: string;
|
|
20
|
+
mediaType?: string;
|
|
21
|
+
} | {
|
|
22
|
+
type: 'file';
|
|
23
|
+
data?: string;
|
|
24
|
+
url?: string;
|
|
25
|
+
mediaType: string;
|
|
26
|
+
filename?: string;
|
|
27
|
+
};
|
|
13
28
|
export interface AIToolCall {
|
|
14
29
|
id: string;
|
|
15
30
|
name: string;
|
|
@@ -23,7 +38,7 @@ export interface AIToolResult {
|
|
|
23
38
|
export interface AIMessage {
|
|
24
39
|
id: string;
|
|
25
40
|
role: 'system' | 'user' | 'assistant' | 'tool';
|
|
26
|
-
content?: string;
|
|
41
|
+
content?: string | AIContentPart[];
|
|
27
42
|
toolCalls?: AIToolCall[];
|
|
28
43
|
toolResults?: AIToolResult[];
|
|
29
44
|
createdAt: Date;
|
|
@@ -39,10 +54,18 @@ export interface AIAgentStep {
|
|
|
39
54
|
result: string;
|
|
40
55
|
}[];
|
|
41
56
|
}
|
|
57
|
+
export interface AIAgentInputAttachment {
|
|
58
|
+
type: 'image' | 'file';
|
|
59
|
+
data?: string;
|
|
60
|
+
url?: string;
|
|
61
|
+
mediaType?: string;
|
|
62
|
+
filename?: string;
|
|
63
|
+
}
|
|
42
64
|
export interface AIAgentInput {
|
|
43
65
|
message: string;
|
|
44
66
|
threadId: string;
|
|
45
67
|
resourceId: string;
|
|
68
|
+
attachments?: AIAgentInputAttachment[];
|
|
46
69
|
}
|
|
47
70
|
export interface AIAgentOutput {
|
|
48
71
|
runId: string;
|
|
@@ -77,7 +100,7 @@ export interface PikkuAIMiddlewareHooks<State extends Record<string, unknown> =
|
|
|
77
100
|
event: AIStreamEvent;
|
|
78
101
|
allEvents: readonly AIStreamEvent[];
|
|
79
102
|
state: State;
|
|
80
|
-
}) => Promise<AIStreamEvent | null> | AIStreamEvent | null;
|
|
103
|
+
}) => Promise<AIStreamEvent | AIStreamEvent[] | null> | AIStreamEvent | AIStreamEvent[] | null;
|
|
81
104
|
modifyOutput?: (services: Services, ctx: {
|
|
82
105
|
text: string;
|
|
83
106
|
messages: AIMessage[];
|
|
@@ -235,6 +258,16 @@ export type AIStreamEvent = {
|
|
|
235
258
|
message: string;
|
|
236
259
|
agent?: string;
|
|
237
260
|
session?: string;
|
|
261
|
+
} | {
|
|
262
|
+
type: 'audio-delta';
|
|
263
|
+
data: string;
|
|
264
|
+
format: string;
|
|
265
|
+
agent?: string;
|
|
266
|
+
session?: string;
|
|
267
|
+
} | {
|
|
268
|
+
type: 'audio-done';
|
|
269
|
+
agent?: string;
|
|
270
|
+
session?: string;
|
|
238
271
|
} | {
|
|
239
272
|
type: 'suspended';
|
|
240
273
|
reason: 'rpc-missing';
|
|
@@ -4,4 +4,4 @@ export { streamAIAgent, resumeAIAgent } from './ai-agent-stream.js';
|
|
|
4
4
|
export { createAssistantUIChannel, parseAssistantUIInput, } from './ai-agent-assistant-ui.js';
|
|
5
5
|
export { type RunAIAgentParams, type StreamAIAgentOptions, ToolApprovalRequired, } from './ai-agent-prepare.js';
|
|
6
6
|
export { addAIAgent, approveAIAgent, getAIAgents, getAIAgentsMeta, } from './ai-agent-registry.js';
|
|
7
|
-
export type { AIAgentInput, AIAgentMeta, AIAgentMemoryConfig, AIAgentStep, AgentRunRow, AgentRunService, AgentRunState, AIMessage, AIStreamChannel, AIStreamEvent, AIThread, CoreAIAgent, PendingApproval, PikkuAIMiddlewareHooks, } from './ai-agent.types.js';
|
|
7
|
+
export type { AIAgentInput, AIAgentInputAttachment, AIAgentMeta, AIAgentMemoryConfig, AIAgentStep, AIContentPart, AgentRunRow, AgentRunService, AgentRunState, AIMessage, AIStreamChannel, AIStreamEvent, AIThread, CoreAIAgent, PendingApproval, PikkuAIMiddlewareHooks, } from './ai-agent.types.js';
|
|
@@ -1,4 +1,7 @@
|
|
|
1
1
|
import type { CoreServices, CoreUserSession } from '../../types/core.types.js';
|
|
2
|
-
import type { CoreChannel, PikkuChannelHandler } from './channel.types.js';
|
|
2
|
+
import type { BinaryData, CoreChannel, PikkuChannelHandler } from './channel.types.js';
|
|
3
3
|
import type { SessionService } from '../../services/user-session-service.js';
|
|
4
|
-
export declare const processMessageHandlers: (services: CoreServices, channelConfig: CoreChannel<any, any>, channelHandler: PikkuChannelHandler, userSession?: SessionService<CoreUserSession>) =>
|
|
4
|
+
export declare const processMessageHandlers: (services: CoreServices, channelConfig: CoreChannel<any, any>, channelHandler: PikkuChannelHandler, userSession?: SessionService<CoreUserSession>) => {
|
|
5
|
+
onMessage: (rawData: unknown) => Promise<unknown>;
|
|
6
|
+
onBinaryMessage: ((data: BinaryData) => Promise<BinaryData | void>) | undefined;
|
|
7
|
+
};
|
|
@@ -91,7 +91,7 @@ export const processMessageHandlers = (services, channelConfig, channelHandler,
|
|
|
91
91
|
},
|
|
92
92
|
});
|
|
93
93
|
};
|
|
94
|
-
|
|
94
|
+
const onMessage = async (rawData) => {
|
|
95
95
|
let result;
|
|
96
96
|
let processed = false;
|
|
97
97
|
// Route-specific handling
|
|
@@ -134,4 +134,14 @@ export const processMessageHandlers = (services, channelConfig, channelHandler,
|
|
|
134
134
|
}
|
|
135
135
|
return result;
|
|
136
136
|
};
|
|
137
|
+
const onBinaryMessage = channelConfig.onBinaryMessage
|
|
138
|
+
? async (data) => {
|
|
139
|
+
if (!validateAuth(requiresSession, userSession?.get(), channelConfig)) {
|
|
140
|
+
logger.error(`Channel ${channelConfig.name} with id ${channelHandler.getChannel().channelId} requires a session for binary message`);
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
return channelConfig.onBinaryMessage(services, data, channelHandler.getChannel());
|
|
144
|
+
}
|
|
145
|
+
: undefined;
|
|
146
|
+
return { onMessage, onBinaryMessage };
|
|
137
147
|
};
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { PikkuError } from '../../errors/error-handler.js';
|
|
2
2
|
import type { HTTPFunctionMetaInputTypes, PikkuHTTPRequest, PikkuHTTPResponse } from '../http/http.types.js';
|
|
3
3
|
import type { CorePikkuMiddleware, MiddlewareMetadata, PermissionMetadata } from '../../types/core.types.js';
|
|
4
|
+
export type BinaryData = ArrayBuffer | Uint8Array;
|
|
4
5
|
export type CorePikkuChannelMiddleware<Services = any, Event = unknown> = (services: Services, event: Event, next: (event: Event | Event[] | null) => Promise<void> | void) => Promise<void> | void;
|
|
5
6
|
export type CorePikkuChannelMiddlewareFactory<In = any, Services = any, Event = unknown> = (input: In) => CorePikkuChannelMiddleware<Services, Event>;
|
|
6
7
|
import type { CorePermissionGroup, CorePikkuFunction, CorePikkuFunctionConfig, CorePikkuFunctionSessionless, CorePikkuPermission } from '../../function/functions.types.js';
|
|
@@ -36,6 +37,7 @@ export interface ChannelMeta {
|
|
|
36
37
|
disconnect: ChannelMessageMeta | null;
|
|
37
38
|
message: ChannelMessageMeta | null;
|
|
38
39
|
messageWirings: Record<string, Record<string, ChannelMessageMeta>>;
|
|
40
|
+
binary?: boolean | null;
|
|
39
41
|
summary?: string;
|
|
40
42
|
description?: string;
|
|
41
43
|
errors?: string[];
|
|
@@ -67,6 +69,8 @@ export type CoreChannel<ChannelData, Channel extends string, ChannelConnect = Co
|
|
|
67
69
|
channelMiddleware?: Array<CorePikkuChannelMiddleware | CorePikkuChannelMiddlewareFactory>;
|
|
68
70
|
permissions?: CorePermissionGroup<PikkuPermission>;
|
|
69
71
|
auth?: boolean;
|
|
72
|
+
binary?: boolean | null;
|
|
73
|
+
onBinaryMessage?: (services: any, data: BinaryData, channel: PikkuChannel<ChannelData, any>) => Promise<BinaryData | void> | BinaryData | void;
|
|
70
74
|
docs?: Partial<{
|
|
71
75
|
description: string;
|
|
72
76
|
response: string;
|
|
@@ -79,11 +83,13 @@ export interface PikkuChannel<OpeningData, out Out> {
|
|
|
79
83
|
channelId: string;
|
|
80
84
|
openingData: OpeningData;
|
|
81
85
|
send(data: Out, isBinary?: boolean): Promise<void> | void;
|
|
86
|
+
sendBinary(data: BinaryData): Promise<void> | void;
|
|
82
87
|
close(): Promise<void> | void;
|
|
83
88
|
state: 'initial' | 'open' | 'closed';
|
|
84
89
|
}
|
|
85
90
|
export interface PikkuChannelHandler<OpeningData = unknown, Out = unknown> {
|
|
86
91
|
send(message: Out, isBinary?: boolean): Promise<void> | void;
|
|
92
|
+
sendBinary(data: BinaryData): Promise<void> | void;
|
|
87
93
|
getChannel(): PikkuChannel<OpeningData, Out>;
|
|
88
94
|
}
|
|
89
95
|
export type PikkuChannelHandlerFactory<OpeningData = unknown, Out = unknown> = (channelId: string, channelName: string, openingData: OpeningData) => PikkuChannelHandler<OpeningData, Out>;
|
|
@@ -6,5 +6,5 @@ export type { EventHubService } from './eventhub-service.js';
|
|
|
6
6
|
export { ChannelStore } from './channel-store.js';
|
|
7
7
|
export type { Channel } from './channel-store.js';
|
|
8
8
|
export { EventHubStore } from './eventhub-store.js';
|
|
9
|
-
export type { ChannelsMeta, CoreChannel, CorePikkuChannelMiddleware, CorePikkuChannelMiddlewareFactory, ChannelMessageMeta, ChannelMeta, PikkuChannel, PikkuChannelHandlerFactory, } from './channel.types.js';
|
|
9
|
+
export type { BinaryData, ChannelsMeta, CoreChannel, CorePikkuChannelMiddleware, CorePikkuChannelMiddlewareFactory, ChannelMessageMeta, ChannelMeta, PikkuChannel, PikkuChannelHandlerFactory, } from './channel.types.js';
|
|
10
10
|
export { defineChannelRoutes } from './define-channel-routes.js';
|
|
@@ -1,15 +1,22 @@
|
|
|
1
|
+
import type { BinaryData } from '../channel.types.js';
|
|
1
2
|
import { PikkuAbstractChannelHandler } from '../pikku-abstract-channel-handler.js';
|
|
2
3
|
export declare class PikkuLocalChannelHandler<OpeningData = unknown, Out = unknown> extends PikkuAbstractChannelHandler<OpeningData, Out> {
|
|
3
4
|
private onMessageCallback?;
|
|
5
|
+
private onBinaryMessageCallback?;
|
|
4
6
|
private openCallBack?;
|
|
5
7
|
private closeCallback?;
|
|
6
8
|
private sendCallback?;
|
|
9
|
+
private sendBinaryCallback?;
|
|
7
10
|
registerOnOpen(callback: () => void): void;
|
|
8
11
|
open(): void;
|
|
9
12
|
registerOnMessage(callback: (data: any) => Promise<unknown>): void;
|
|
10
13
|
message(data: unknown): Promise<unknown>;
|
|
14
|
+
registerOnBinaryMessage(callback: (data: BinaryData) => Promise<BinaryData | void> | BinaryData | void): void;
|
|
15
|
+
binaryMessage(data: BinaryData): Promise<BinaryData | void>;
|
|
11
16
|
registerOnClose(callback: () => void): void;
|
|
12
17
|
close(): void;
|
|
13
18
|
registerOnSend(send: (message: Out) => void): void;
|
|
14
19
|
send(message: Out, isBinary?: boolean): void;
|
|
20
|
+
registerOnSendBinary(send: (data: BinaryData) => void): void;
|
|
21
|
+
sendBinary(data: BinaryData): void;
|
|
15
22
|
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { PikkuAbstractChannelHandler } from '../pikku-abstract-channel-handler.js';
|
|
2
2
|
export class PikkuLocalChannelHandler extends PikkuAbstractChannelHandler {
|
|
3
3
|
onMessageCallback;
|
|
4
|
+
onBinaryMessageCallback;
|
|
4
5
|
openCallBack;
|
|
5
6
|
closeCallback;
|
|
6
7
|
sendCallback;
|
|
8
|
+
sendBinaryCallback;
|
|
7
9
|
registerOnOpen(callback) {
|
|
8
10
|
this.openCallBack = callback;
|
|
9
11
|
}
|
|
@@ -19,6 +21,12 @@ export class PikkuLocalChannelHandler extends PikkuAbstractChannelHandler {
|
|
|
19
21
|
async message(data) {
|
|
20
22
|
return this.onMessageCallback?.(data);
|
|
21
23
|
}
|
|
24
|
+
registerOnBinaryMessage(callback) {
|
|
25
|
+
this.onBinaryMessageCallback = callback;
|
|
26
|
+
}
|
|
27
|
+
async binaryMessage(data) {
|
|
28
|
+
return this.onBinaryMessageCallback?.(data);
|
|
29
|
+
}
|
|
22
30
|
registerOnClose(callback) {
|
|
23
31
|
this.closeCallback = callback;
|
|
24
32
|
}
|
|
@@ -38,4 +46,13 @@ export class PikkuLocalChannelHandler extends PikkuAbstractChannelHandler {
|
|
|
38
46
|
}
|
|
39
47
|
return this.sendCallback?.(message, isBinary);
|
|
40
48
|
}
|
|
49
|
+
registerOnSendBinary(send) {
|
|
50
|
+
this.sendBinaryCallback = send;
|
|
51
|
+
}
|
|
52
|
+
sendBinary(data) {
|
|
53
|
+
if (!this.sendBinaryCallback) {
|
|
54
|
+
throw new Error('No sendBinary callback registered');
|
|
55
|
+
}
|
|
56
|
+
this.sendBinaryCallback(data);
|
|
57
|
+
}
|
|
41
58
|
}
|
|
@@ -99,7 +99,7 @@ export const runLocalChannel = async ({ channelId, request, response, route, ski
|
|
|
99
99
|
}
|
|
100
100
|
await closeServices();
|
|
101
101
|
});
|
|
102
|
-
const onMessage = processMessageHandlers(services, channelConfig, channelHandler, userSession);
|
|
102
|
+
const { onMessage, onBinaryMessage } = processMessageHandlers(services, channelConfig, channelHandler, userSession);
|
|
103
103
|
channelHandler.registerOnMessage(async (data) => {
|
|
104
104
|
try {
|
|
105
105
|
const result = await onMessage(data);
|
|
@@ -110,6 +110,19 @@ export const runLocalChannel = async ({ channelId, request, response, route, ski
|
|
|
110
110
|
channel.send({ error: e.message || 'Unknown error' });
|
|
111
111
|
}
|
|
112
112
|
});
|
|
113
|
+
if (onBinaryMessage) {
|
|
114
|
+
channelHandler.registerOnBinaryMessage(async (data) => {
|
|
115
|
+
try {
|
|
116
|
+
const result = await onBinaryMessage(data);
|
|
117
|
+
if (result) {
|
|
118
|
+
await channel.sendBinary(result);
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
catch (e) {
|
|
122
|
+
singletonServices.logger.error(`Error handling binary message: ${e.message}`);
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
}
|
|
113
126
|
}
|
|
114
127
|
catch (e) {
|
|
115
128
|
handleHTTPError(e, http, channelId, singletonServices.logger, logWarningsForStatusCodes, respondWith404, bubbleErrors);
|