@pikku/core 0.12.13 → 0.12.14
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/errors/errors.d.ts +14 -0
- package/dist/errors/errors.js +21 -0
- package/dist/function/function-runner.d.ts +1 -1
- package/dist/function/function-runner.js +8 -4
- package/dist/pikku-state.js +1 -0
- package/dist/services/credential-service.d.ts +11 -0
- package/dist/services/credential-wire-service.d.ts +14 -3
- package/dist/services/credential-wire-service.js +49 -6
- package/dist/services/local-credential-service.d.ts +2 -0
- package/dist/services/local-credential-service.js +20 -0
- package/dist/services/pikku-user-id.d.ts +3 -0
- package/dist/services/pikku-user-id.js +16 -0
- package/dist/services/typed-credential-service.d.ts +2 -0
- package/dist/services/typed-credential-service.js +6 -0
- package/dist/types/core.types.d.ts +6 -2
- package/dist/types/state.types.d.ts +7 -0
- package/dist/wirings/ai-agent/ai-agent-prepare.d.ts +21 -0
- package/dist/wirings/ai-agent/ai-agent-prepare.js +48 -0
- package/dist/wirings/ai-agent/ai-agent-runner.js +6 -1
- package/dist/wirings/ai-agent/ai-agent-stream.d.ts +2 -1
- package/dist/wirings/ai-agent/ai-agent-stream.js +60 -3
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +20 -1
- package/dist/wirings/ai-agent/index.d.ts +1 -1
- package/dist/wirings/ai-agent/index.js +1 -1
- package/dist/wirings/cli/cli-runner.js +1 -1
- package/dist/wirings/http/http-runner.js +0 -4
- package/dist/wirings/queue/queue-runner.js +1 -0
- package/dist/wirings/queue/queue.types.d.ts +6 -0
- package/dist/wirings/rpc/rpc-runner.js +1 -0
- package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +2 -0
- package/dist/wirings/workflow/pikku-workflow-service.js +2 -0
- package/dist/wirings/workflow/workflow.types.d.ts +2 -0
- package/package.json +1 -1
- package/src/errors/errors.ts +32 -0
- package/src/function/function-runner.ts +19 -9
- package/src/pikku-state.ts +1 -0
- package/src/services/credential-service.ts +13 -0
- package/src/services/credential-wire-service.test.ts +174 -0
- package/src/services/credential-wire-service.ts +52 -8
- package/src/services/local-credential-service.ts +22 -0
- package/src/services/pikku-user-id.test.ts +62 -0
- package/src/services/pikku-user-id.ts +17 -0
- package/src/services/typed-credential-service.ts +8 -0
- package/src/types/core.types.ts +8 -2
- package/src/types/state.types.ts +5 -0
- package/src/wirings/ai-agent/ai-agent-prepare.ts +71 -1
- package/src/wirings/ai-agent/ai-agent-runner.ts +6 -2
- package/src/wirings/ai-agent/ai-agent-stream.ts +107 -3
- package/src/wirings/ai-agent/ai-agent.types.ts +22 -1
- package/src/wirings/ai-agent/index.ts +1 -0
- package/src/wirings/channel/channel-handler.ts +0 -1
- package/src/wirings/cli/cli-runner.ts +1 -1
- package/src/wirings/http/http-runner.ts +0 -7
- package/src/wirings/mcp/mcp-runner.ts +0 -1
- package/src/wirings/queue/queue-runner.ts +1 -1
- package/src/wirings/queue/queue.types.ts +6 -0
- package/src/wirings/rpc/rpc-runner.ts +9 -3
- package/src/wirings/workflow/dsl/workflow-dsl.types.ts +2 -0
- package/src/wirings/workflow/pikku-workflow-service.ts +2 -0
- package/src/wirings/workflow/workflow.types.ts +2 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -70,6 +70,75 @@ export class ToolApprovalRequired extends PikkuError {
|
|
|
70
70
|
}
|
|
71
71
|
}
|
|
72
72
|
|
|
73
|
+
export class ToolCredentialRequired extends PikkuError {
|
|
74
|
+
public readonly toolCallId: string
|
|
75
|
+
public readonly toolName: string
|
|
76
|
+
public readonly args: unknown
|
|
77
|
+
public readonly credentialName: string
|
|
78
|
+
public readonly credentialType: 'oauth2' | 'apikey'
|
|
79
|
+
public readonly connectUrl?: string
|
|
80
|
+
|
|
81
|
+
constructor(
|
|
82
|
+
toolCallId: string,
|
|
83
|
+
toolName: string,
|
|
84
|
+
args: unknown,
|
|
85
|
+
credentialName: string,
|
|
86
|
+
credentialType: 'oauth2' | 'apikey',
|
|
87
|
+
connectUrl?: string
|
|
88
|
+
) {
|
|
89
|
+
super(`Tool '${toolName}' requires credential '${credentialName}'`)
|
|
90
|
+
this.toolCallId = toolCallId
|
|
91
|
+
this.toolName = toolName
|
|
92
|
+
this.args = args
|
|
93
|
+
this.credentialName = credentialName
|
|
94
|
+
this.credentialType = credentialType
|
|
95
|
+
this.connectUrl = connectUrl
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface AddonCredentialRequirement {
|
|
100
|
+
credentialName: string
|
|
101
|
+
displayName: string
|
|
102
|
+
addonNamespace: string
|
|
103
|
+
type: 'wire'
|
|
104
|
+
oauth2: boolean
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Given a list of tool names (e.g. ["oauth-api:getProfile"]),
|
|
109
|
+
* returns the wire OAuth credentials required by their addons.
|
|
110
|
+
*/
|
|
111
|
+
export function getAddonCredentialRequirements(
|
|
112
|
+
toolNames: string[]
|
|
113
|
+
): AddonCredentialRequirement[] {
|
|
114
|
+
const requirements = new Map<string, AddonCredentialRequirement>()
|
|
115
|
+
|
|
116
|
+
for (const toolName of toolNames) {
|
|
117
|
+
if (!toolName.includes(':')) continue
|
|
118
|
+
const resolved = resolveNamespace(toolName)
|
|
119
|
+
if (!resolved) continue
|
|
120
|
+
|
|
121
|
+
const credsMeta = pikkuState(resolved.package, 'package', 'credentialsMeta')
|
|
122
|
+
if (!credsMeta) continue
|
|
123
|
+
|
|
124
|
+
for (const [name, meta] of Object.entries(
|
|
125
|
+
credsMeta as Record<string, any>
|
|
126
|
+
)) {
|
|
127
|
+
if (meta.type === 'wire' && meta.oauth2 && !requirements.has(name)) {
|
|
128
|
+
requirements.set(name, {
|
|
129
|
+
credentialName: name,
|
|
130
|
+
displayName: meta.displayName ?? name,
|
|
131
|
+
addonNamespace: toolName.split(':')[0],
|
|
132
|
+
type: 'wire',
|
|
133
|
+
oauth2: true,
|
|
134
|
+
})
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return [...requirements.values()]
|
|
140
|
+
}
|
|
141
|
+
|
|
73
142
|
export type StreamContext = {
|
|
74
143
|
channel: AIStreamChannel
|
|
75
144
|
options?: StreamAIAgentOptions
|
|
@@ -478,8 +547,9 @@ export async function buildToolDefs(
|
|
|
478
547
|
let execError: unknown
|
|
479
548
|
try {
|
|
480
549
|
result = await originalExecute(args)
|
|
481
|
-
} catch (err) {
|
|
550
|
+
} catch (err: any) {
|
|
482
551
|
execError = err
|
|
552
|
+
if (err?.payload?.error === 'missing_credential') throw err
|
|
483
553
|
result = err instanceof Error ? err.message : String(err)
|
|
484
554
|
}
|
|
485
555
|
const durationMs = Date.now() - startTime
|
|
@@ -430,8 +430,12 @@ export async function resumeAIAgentSync(
|
|
|
430
430
|
typeof toolResult === 'string'
|
|
431
431
|
? toolResult
|
|
432
432
|
: JSON.stringify(toolResult)
|
|
433
|
-
} catch (err) {
|
|
434
|
-
|
|
433
|
+
} catch (err: any) {
|
|
434
|
+
if (err?.payload?.error === 'missing_credential') {
|
|
435
|
+
resultStr = JSON.stringify(err.payload)
|
|
436
|
+
} else {
|
|
437
|
+
resultStr = `Error: ${err instanceof Error ? err.message : String(err)}`
|
|
438
|
+
}
|
|
435
439
|
}
|
|
436
440
|
} else {
|
|
437
441
|
continue
|
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
buildToolDefs,
|
|
37
37
|
createScopedChannel,
|
|
38
38
|
ToolApprovalRequired,
|
|
39
|
+
ToolCredentialRequired,
|
|
39
40
|
type RunAIAgentParams,
|
|
40
41
|
type StreamAIAgentOptions,
|
|
41
42
|
type StreamContext,
|
|
@@ -236,6 +237,7 @@ type StepLoopParams = {
|
|
|
236
237
|
type StepLoopResult =
|
|
237
238
|
| { outcome: 'done' }
|
|
238
239
|
| { outcome: 'approval'; approvals: ToolApprovalRequired[] }
|
|
240
|
+
| { outcome: 'credential'; credentialRequests: ToolCredentialRequired[] }
|
|
239
241
|
|
|
240
242
|
async function runStreamStepLoop(
|
|
241
243
|
params: StepLoopParams
|
|
@@ -310,6 +312,13 @@ async function runStreamStepLoop(
|
|
|
310
312
|
return { outcome: 'approval', approvals: approvalsNeeded }
|
|
311
313
|
}
|
|
312
314
|
|
|
315
|
+
const credentialRequests = checkForCredentialRequests(stepResult, runId)
|
|
316
|
+
if (credentialRequests.length > 0) {
|
|
317
|
+
// Append step messages so the tool result is preserved for resume
|
|
318
|
+
appendStepMessages(runnerParams, stepResult)
|
|
319
|
+
return { outcome: 'credential', credentialRequests }
|
|
320
|
+
}
|
|
321
|
+
|
|
313
322
|
appendStepMessages(runnerParams, stepResult)
|
|
314
323
|
}
|
|
315
324
|
|
|
@@ -386,6 +395,40 @@ export function checkForApprovals(
|
|
|
386
395
|
return approvals
|
|
387
396
|
}
|
|
388
397
|
|
|
398
|
+
export function checkForCredentialRequests(
|
|
399
|
+
stepResult: AIAgentStepResult,
|
|
400
|
+
runId: string
|
|
401
|
+
): ToolCredentialRequired[] {
|
|
402
|
+
const requests: ToolCredentialRequired[] = []
|
|
403
|
+
for (const tr of stepResult.toolResults) {
|
|
404
|
+
if (
|
|
405
|
+
tr.result &&
|
|
406
|
+
typeof tr.result === 'object' &&
|
|
407
|
+
'__credentialRequired' in (tr.result as object)
|
|
408
|
+
) {
|
|
409
|
+
const r = tr.result as {
|
|
410
|
+
credentialName: string
|
|
411
|
+
credentialType: 'oauth2' | 'apikey'
|
|
412
|
+
connectUrl?: string
|
|
413
|
+
}
|
|
414
|
+
const tc = stepResult.toolCalls.find(
|
|
415
|
+
(t) => t.toolCallId === tr.toolCallId
|
|
416
|
+
)
|
|
417
|
+
requests.push(
|
|
418
|
+
new ToolCredentialRequired(
|
|
419
|
+
tr.toolCallId,
|
|
420
|
+
tc?.toolName ?? 'unknown',
|
|
421
|
+
tc?.args ?? {},
|
|
422
|
+
r.credentialName,
|
|
423
|
+
r.credentialType,
|
|
424
|
+
r.connectUrl
|
|
425
|
+
)
|
|
426
|
+
)
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
return requests
|
|
430
|
+
}
|
|
431
|
+
|
|
389
432
|
export function appendStepMessages(
|
|
390
433
|
runnerParams: AIAgentRunnerParams,
|
|
391
434
|
stepResult: AIAgentStepResult
|
|
@@ -472,6 +515,40 @@ function handleApprovals(
|
|
|
472
515
|
})()
|
|
473
516
|
}
|
|
474
517
|
|
|
518
|
+
function handleCredentialRequests(
|
|
519
|
+
requests: ToolCredentialRequired[],
|
|
520
|
+
runId: string,
|
|
521
|
+
channel: AIStreamChannel,
|
|
522
|
+
aiRunState: AIRunStateService,
|
|
523
|
+
persistingChannel: PersistingChannel
|
|
524
|
+
): Promise<void> {
|
|
525
|
+
return (async () => {
|
|
526
|
+
await persistingChannel.flush()
|
|
527
|
+
|
|
528
|
+
const pendingApprovals = requests.map((req) => ({
|
|
529
|
+
type: 'credential-request' as const,
|
|
530
|
+
toolCallId: req.toolCallId,
|
|
531
|
+
toolName: req.toolName,
|
|
532
|
+
args: req.args,
|
|
533
|
+
credentialName: req.credentialName,
|
|
534
|
+
credentialType: req.credentialType,
|
|
535
|
+
connectUrl: req.connectUrl,
|
|
536
|
+
}))
|
|
537
|
+
|
|
538
|
+
await aiRunState.updateRun(runId, {
|
|
539
|
+
status: 'suspended',
|
|
540
|
+
suspendReason: 'credential',
|
|
541
|
+
pendingApprovals,
|
|
542
|
+
})
|
|
543
|
+
|
|
544
|
+
// Don't send credential-request SSE events — the tool result with
|
|
545
|
+
// __credentialRequired was already streamed. The frontend detects it
|
|
546
|
+
// from the tool result and shows Connect/Ignore buttons.
|
|
547
|
+
channel.send({ type: 'done' })
|
|
548
|
+
channel.close()
|
|
549
|
+
})()
|
|
550
|
+
}
|
|
551
|
+
|
|
475
552
|
export async function streamAIAgent(
|
|
476
553
|
agentName: string,
|
|
477
554
|
input: {
|
|
@@ -665,6 +742,17 @@ export async function streamAIAgent(
|
|
|
665
742
|
return persistingChannel.fullText
|
|
666
743
|
}
|
|
667
744
|
|
|
745
|
+
if (loopResult.outcome === 'credential') {
|
|
746
|
+
await handleCredentialRequests(
|
|
747
|
+
loopResult.credentialRequests,
|
|
748
|
+
runId,
|
|
749
|
+
channel,
|
|
750
|
+
aiRunState,
|
|
751
|
+
persistingChannel
|
|
752
|
+
)
|
|
753
|
+
return persistingChannel.fullText
|
|
754
|
+
}
|
|
755
|
+
|
|
668
756
|
await postStreamCleanup(
|
|
669
757
|
persistingChannel,
|
|
670
758
|
aiMiddlewares,
|
|
@@ -769,7 +857,8 @@ export async function resumeAIAgent(
|
|
|
769
857
|
{
|
|
770
858
|
id: input.toolCallId,
|
|
771
859
|
name:
|
|
772
|
-
pending.type === 'tool-call'
|
|
860
|
+
pending.type === 'tool-call' ||
|
|
861
|
+
pending.type === 'credential-request'
|
|
773
862
|
? pending.toolName
|
|
774
863
|
: pending.agentName,
|
|
775
864
|
result: denialResult,
|
|
@@ -896,8 +985,12 @@ export async function resumeAIAgent(
|
|
|
896
985
|
let isError = false
|
|
897
986
|
try {
|
|
898
987
|
toolResult = await matchingTool.execute(toolArgs)
|
|
899
|
-
} catch (execErr) {
|
|
900
|
-
|
|
988
|
+
} catch (execErr: any) {
|
|
989
|
+
if (execErr?.payload?.error === 'missing_credential') {
|
|
990
|
+
toolResult = execErr.payload
|
|
991
|
+
} else {
|
|
992
|
+
toolResult = `Error: ${execErr instanceof Error ? execErr.message : String(execErr)}`
|
|
993
|
+
}
|
|
901
994
|
isError = true
|
|
902
995
|
}
|
|
903
996
|
|
|
@@ -1111,6 +1204,17 @@ async function continueAfterToolResult(
|
|
|
1111
1204
|
return
|
|
1112
1205
|
}
|
|
1113
1206
|
|
|
1207
|
+
if (loopResult.outcome === 'credential') {
|
|
1208
|
+
await handleCredentialRequests(
|
|
1209
|
+
loopResult.credentialRequests,
|
|
1210
|
+
run.runId,
|
|
1211
|
+
channel,
|
|
1212
|
+
aiRunState,
|
|
1213
|
+
persistingChannel
|
|
1214
|
+
)
|
|
1215
|
+
return
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1114
1218
|
await postStreamCleanup(
|
|
1115
1219
|
persistingChannel,
|
|
1116
1220
|
aiMiddlewares,
|
|
@@ -263,6 +263,18 @@ export type AIStreamEvent =
|
|
|
263
263
|
agent?: string
|
|
264
264
|
session?: string
|
|
265
265
|
}
|
|
266
|
+
| {
|
|
267
|
+
type: 'credential-request'
|
|
268
|
+
toolCallId: string
|
|
269
|
+
toolName: string
|
|
270
|
+
args: unknown
|
|
271
|
+
credentialName: string
|
|
272
|
+
credentialType: 'oauth2' | 'apikey'
|
|
273
|
+
connectUrl?: string
|
|
274
|
+
runId: string
|
|
275
|
+
agent?: string
|
|
276
|
+
session?: string
|
|
277
|
+
}
|
|
266
278
|
| {
|
|
267
279
|
type: 'usage'
|
|
268
280
|
tokens: { input: number; output: number }
|
|
@@ -310,6 +322,15 @@ export type PendingApproval =
|
|
|
310
322
|
displayToolName: string
|
|
311
323
|
displayArgs: unknown
|
|
312
324
|
}
|
|
325
|
+
| {
|
|
326
|
+
type: 'credential-request'
|
|
327
|
+
toolCallId: string
|
|
328
|
+
toolName: string
|
|
329
|
+
args: unknown
|
|
330
|
+
credentialName: string
|
|
331
|
+
credentialType: 'oauth2' | 'apikey'
|
|
332
|
+
connectUrl?: string
|
|
333
|
+
}
|
|
313
334
|
|
|
314
335
|
export interface AgentRunState {
|
|
315
336
|
runId: string
|
|
@@ -318,7 +339,7 @@ export interface AgentRunState {
|
|
|
318
339
|
resourceId: string
|
|
319
340
|
status: 'running' | 'suspended' | 'completed' | 'failed'
|
|
320
341
|
errorMessage?: string
|
|
321
|
-
suspendReason?: 'approval' | 'rpc-missing'
|
|
342
|
+
suspendReason?: 'approval' | 'credential' | 'rpc-missing'
|
|
322
343
|
missingRpcs?: string[]
|
|
323
344
|
pendingApprovals?: PendingApproval[]
|
|
324
345
|
usage: { inputTokens: number; outputTokens: number; model: string }
|
|
@@ -13,7 +13,6 @@ import { pikkuState } from '../../pikku-state.js'
|
|
|
13
13
|
import { runPikkuFunc } from '../../function/function-runner.js'
|
|
14
14
|
import type { SessionService } from '../../services/user-session-service.js'
|
|
15
15
|
import { createMiddlewareSessionWireProps } from '../../services/user-session-service.js'
|
|
16
|
-
|
|
17
16
|
const getChannelMeta = (channelName: string) => {
|
|
18
17
|
return pikkuState(null, 'channel', 'meta')[channelName]
|
|
19
18
|
}
|
|
@@ -191,7 +191,7 @@ function registerCLICommands(
|
|
|
191
191
|
}
|
|
192
192
|
}
|
|
193
193
|
|
|
194
|
-
addFunction(funcName, unwrapFunc(command))
|
|
194
|
+
addFunction(funcName, unwrapFunc(command), currentMeta?.packageName)
|
|
195
195
|
|
|
196
196
|
// Register renderer if provided
|
|
197
197
|
if (typeof command === 'object' && command.render) {
|
|
@@ -33,10 +33,6 @@ import {
|
|
|
33
33
|
} from '../../pikku-state.js'
|
|
34
34
|
import { PikkuSessionService } from '../../services/user-session-service.js'
|
|
35
35
|
import { getErrorResponse } from '../../errors/error-handler.js'
|
|
36
|
-
import {
|
|
37
|
-
PikkuCredentialWireService,
|
|
38
|
-
createMiddlewareCredentialWireProps,
|
|
39
|
-
} from '../../services/credential-wire-service.js'
|
|
40
36
|
import { handleHTTPError } from '../../handle-error.js'
|
|
41
37
|
import { pikkuState } from '../../pikku-state.js'
|
|
42
38
|
import { PikkuFetchHTTPResponse } from './pikku-fetch-http-response.js'
|
|
@@ -303,7 +299,6 @@ const executeRoute = async (
|
|
|
303
299
|
}
|
|
304
300
|
) => {
|
|
305
301
|
const userSession = new PikkuSessionService<CoreUserSession>()
|
|
306
|
-
const credentialWire = new PikkuCredentialWireService()
|
|
307
302
|
const { params, route, meta } = matchedRoute
|
|
308
303
|
const { singletonServices, createWireServices, skipUserSession, requestId } =
|
|
309
304
|
services
|
|
@@ -382,7 +377,6 @@ const executeRoute = async (
|
|
|
382
377
|
setSession: (s: any) => userSession.setInitial(s),
|
|
383
378
|
getSession: () => userSession.get(),
|
|
384
379
|
hasSessionChanged: () => userSession.sessionChanged,
|
|
385
|
-
...createMiddlewareCredentialWireProps(credentialWire),
|
|
386
380
|
}
|
|
387
381
|
|
|
388
382
|
result = await runPikkuFunc(
|
|
@@ -402,7 +396,6 @@ const executeRoute = async (
|
|
|
402
396
|
tags: route.tags,
|
|
403
397
|
wire,
|
|
404
398
|
sessionService: userSession,
|
|
405
|
-
credentialWireService: credentialWire,
|
|
406
399
|
packageName: meta.packageName,
|
|
407
400
|
}
|
|
408
401
|
)
|
|
@@ -29,7 +29,6 @@ import {
|
|
|
29
29
|
PikkuSessionService,
|
|
30
30
|
createMiddlewareSessionWireProps,
|
|
31
31
|
} from '../../services/user-session-service.js'
|
|
32
|
-
|
|
33
32
|
export class MCPError extends Error {
|
|
34
33
|
constructor(public readonly error: JsonRpcErrorResponse) {
|
|
35
34
|
super(error?.message || 'MCP Error')
|
|
@@ -12,7 +12,6 @@ import {
|
|
|
12
12
|
pikkuState,
|
|
13
13
|
} from '../../pikku-state.js'
|
|
14
14
|
import { addFunction, runPikkuFunc } from '../../function/function-runner.js'
|
|
15
|
-
|
|
16
15
|
/**
|
|
17
16
|
* Error class for queue processor not found
|
|
18
17
|
*/
|
|
@@ -132,6 +131,7 @@ export async function runQueueJob({
|
|
|
132
131
|
const queue: PikkuQueue = {
|
|
133
132
|
queueName: job.queueName,
|
|
134
133
|
jobId: job.id,
|
|
134
|
+
pikkuUserId: job.pikkuUserId,
|
|
135
135
|
updateProgress:
|
|
136
136
|
updateProgress ||
|
|
137
137
|
(async (progress: number | string | object) => {
|
|
@@ -96,6 +96,8 @@ export interface QueueJob<T = any, R = any> {
|
|
|
96
96
|
result?: R
|
|
97
97
|
waitForCompletion?: (ttl?: number) => Promise<R>
|
|
98
98
|
metadata?: () => Promise<QueueJobMetadata> | QueueJobMetadata
|
|
99
|
+
/** Pikku user ID propagated from the job producer */
|
|
100
|
+
pikkuUserId?: string
|
|
99
101
|
}
|
|
100
102
|
|
|
101
103
|
/**
|
|
@@ -109,6 +111,8 @@ export interface JobOptions {
|
|
|
109
111
|
removeOnComplete?: number
|
|
110
112
|
removeOnFail?: number
|
|
111
113
|
jobId?: string
|
|
114
|
+
/** Pikku user ID to propagate to the queue worker for credential resolution */
|
|
115
|
+
pikkuUserId?: string
|
|
112
116
|
}
|
|
113
117
|
|
|
114
118
|
/**
|
|
@@ -180,6 +184,8 @@ export interface PikkuQueue {
|
|
|
180
184
|
queueName: string
|
|
181
185
|
/** The current job ID */
|
|
182
186
|
jobId: string
|
|
187
|
+
/** Pikku user ID propagated from the job producer for credential resolution */
|
|
188
|
+
pikkuUserId?: string
|
|
183
189
|
/** Update job progress (0-100 or custom value) */
|
|
184
190
|
updateProgress: (progress: number | string | object) => Promise<void>
|
|
185
191
|
/** Fail the current job with optional reason */
|
|
@@ -239,6 +239,7 @@ export class ContextAwareRPCService {
|
|
|
239
239
|
type: this.wire.wireType ?? 'unknown',
|
|
240
240
|
id: this.wire.wireId,
|
|
241
241
|
...(parentRunId ? { parentRunId } : {}),
|
|
242
|
+
...(this.wire.pikkuUserId ? { pikkuUserId: this.wire.pikkuUserId } : {}),
|
|
242
243
|
}
|
|
243
244
|
return this.services.workflowService.startWorkflow(
|
|
244
245
|
workflowName,
|
|
@@ -306,9 +307,14 @@ export class ContextAwareRPCService {
|
|
|
306
307
|
approvals: { toolCallId: string; approved: boolean }[],
|
|
307
308
|
expectedAgentName?: string
|
|
308
309
|
) => {
|
|
309
|
-
const result = await resumeAIAgentSync(
|
|
310
|
-
|
|
311
|
-
|
|
310
|
+
const result = await resumeAIAgentSync(
|
|
311
|
+
runId,
|
|
312
|
+
approvals,
|
|
313
|
+
{
|
|
314
|
+
sessionService: this.options.sessionService,
|
|
315
|
+
},
|
|
316
|
+
expectedAgentName
|
|
317
|
+
)
|
|
312
318
|
return {
|
|
313
319
|
runId: result.runId,
|
|
314
320
|
result: result.object ?? result.text,
|
|
@@ -306,6 +306,8 @@ export interface PikkuWorkflowWire {
|
|
|
306
306
|
name: string
|
|
307
307
|
/** The current run ID */
|
|
308
308
|
runId: string
|
|
309
|
+
/** Pikku user ID propagated from the originating request for credential resolution */
|
|
310
|
+
pikkuUserId?: string
|
|
309
311
|
/** Get the current workflow run */
|
|
310
312
|
getRun: () => Promise<WorkflowRun>
|
|
311
313
|
|
|
@@ -600,8 +600,10 @@ export abstract class PikkuWorkflowService implements WorkflowService {
|
|
|
600
600
|
runId,
|
|
601
601
|
rpcService
|
|
602
602
|
)
|
|
603
|
+
workflowWire.pikkuUserId = run.wire?.pikkuUserId
|
|
603
604
|
const wire: PikkuWire = {
|
|
604
605
|
workflow: workflowWire,
|
|
606
|
+
pikkuUserId: run.wire?.pikkuUserId,
|
|
605
607
|
session: rpcService.wire?.session,
|
|
606
608
|
rpc: rpcService.wire?.rpc,
|
|
607
609
|
}
|
|
@@ -41,6 +41,8 @@ export interface WorkflowRunWire {
|
|
|
41
41
|
id?: string
|
|
42
42
|
parentRunId?: string
|
|
43
43
|
parentStepId?: string
|
|
44
|
+
/** Pikku user ID propagated from the originating request for credential resolution */
|
|
45
|
+
pikkuUserId?: string
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
export interface WorkflowServiceConfig {
|