@pikku/core 0.12.80 → 0.12.83
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 +345 -0
- package/dist/errors/index.d.ts +1 -1
- package/dist/errors/index.js +1 -1
- package/dist/function/function-runner.js +2 -5
- package/dist/function/index.d.ts +1 -1
- package/dist/index.d.ts +11 -11
- package/dist/index.js +3 -3
- package/dist/pikku-state.js +4 -0
- package/dist/services/ai-agent-runner-service.d.ts +7 -0
- package/dist/services/ai-run-state-service.d.ts +10 -0
- package/dist/services/in-memory-ai-run-state-service.d.ts +5 -1
- package/dist/services/in-memory-ai-run-state-service.js +9 -0
- package/dist/services/index.d.ts +15 -16
- package/dist/services/index.js +5 -5
- package/dist/services/meta-service.d.ts +2 -1
- package/dist/services/scoped-credential-service.d.ts +21 -0
- package/dist/services/scoped-credential-service.js +53 -0
- package/dist/testing/service-tests/ai-storage-service-tests.js +76 -0
- package/dist/types/core.types.d.ts +2 -3
- package/dist/types/state.types.d.ts +19 -1
- package/dist/wirings/actor-flow/index.d.ts +1 -1
- package/dist/wirings/ai-agent/ai-agent-finalize.d.ts +58 -0
- package/dist/wirings/ai-agent/ai-agent-finalize.js +138 -0
- package/dist/wirings/ai-agent/ai-agent-interrupt.js +1 -0
- package/dist/wirings/ai-agent/ai-agent-memory.d.ts +2 -8
- package/dist/wirings/ai-agent/ai-agent-memory.js +34 -17
- package/dist/wirings/ai-agent/ai-agent-model-config.d.ts +7 -0
- package/dist/wirings/ai-agent/ai-agent-model-config.js +44 -1
- package/dist/wirings/ai-agent/ai-agent-prepare.js +4 -0
- package/dist/wirings/ai-agent/ai-agent-runner.js +61 -40
- package/dist/wirings/ai-agent/ai-agent-stream.js +89 -36
- package/dist/wirings/ai-agent/ai-agent-turn.d.ts +1 -0
- package/dist/wirings/ai-agent/ai-agent-turn.js +1 -0
- package/dist/wirings/ai-agent/ai-agent.types.d.ts +46 -1
- package/dist/wirings/ai-agent/index.d.ts +8 -7
- package/dist/wirings/ai-agent/index.js +5 -4
- package/dist/wirings/ai-scorer/ai-scorer-grade.d.ts +26 -0
- package/dist/wirings/ai-scorer/ai-scorer-grade.js +33 -0
- package/dist/wirings/ai-scorer/ai-scorer-judge.d.ts +17 -0
- package/dist/wirings/ai-scorer/ai-scorer-judge.js +92 -0
- package/dist/wirings/ai-scorer/ai-scorer-live.d.ts +15 -0
- package/dist/wirings/ai-scorer/ai-scorer-live.js +38 -0
- package/dist/wirings/ai-scorer/ai-scorer-registry.d.ts +18 -0
- package/dist/wirings/ai-scorer/ai-scorer-registry.js +46 -0
- package/dist/wirings/ai-scorer/ai-scorer-sampling.d.ts +8 -0
- package/dist/wirings/ai-scorer/ai-scorer-sampling.js +31 -0
- package/dist/wirings/ai-scorer/ai-scorer-snapshots.d.ts +10 -0
- package/dist/wirings/ai-scorer/ai-scorer-snapshots.js +40 -0
- package/dist/wirings/ai-scorer/ai-scorer-worker.d.ts +15 -0
- package/dist/wirings/ai-scorer/ai-scorer-worker.js +58 -0
- package/dist/wirings/ai-scorer/ai-scorer.d.ts +39 -0
- package/dist/wirings/ai-scorer/ai-scorer.js +40 -0
- package/dist/wirings/ai-scorer/ai-scorer.types.d.ts +90 -0
- package/dist/wirings/ai-scorer/ai-scorer.types.js +4 -0
- package/dist/wirings/ai-scorer/index.d.ts +6 -0
- package/dist/wirings/ai-scorer/index.js +5 -0
- package/dist/wirings/channel/index.d.ts +5 -6
- package/dist/wirings/channel/index.js +3 -4
- package/dist/wirings/channel/local/local-channel-runner.js +8 -1
- package/dist/wirings/cli/channel/cli-raw-channel-runner.js +9 -1
- package/dist/wirings/cli/channel/index.d.ts +1 -2
- package/dist/wirings/cli/channel/index.js +0 -1
- package/dist/wirings/cli/cli-runner.js +13 -1
- package/dist/wirings/credential/index.d.ts +1 -1
- package/dist/wirings/gateway/index.d.ts +1 -1
- package/dist/wirings/http/http-runner.js +8 -2
- package/dist/wirings/http/index.d.ts +1 -2
- package/dist/wirings/mcp/index.d.ts +1 -1
- package/dist/wirings/mcp/mcp-runner.d.ts +15 -0
- package/dist/wirings/mcp/mcp-runner.js +18 -5
- package/dist/wirings/persona/index.d.ts +3 -4
- package/dist/wirings/persona/index.js +2 -3
- package/dist/wirings/queue/index.d.ts +1 -3
- package/dist/wirings/queue/index.js +1 -3
- package/dist/wirings/rpc/addon-runner.d.ts +8 -0
- package/dist/wirings/rpc/addon-runner.js +31 -3
- package/dist/wirings/rpc/rpc-runner.js +4 -0
- package/dist/wirings/rpc/rpc-types.d.ts +8 -0
- package/dist/wirings/rpc/wire-addon.d.ts +25 -0
- package/dist/wirings/rpc/wire-addon.js +8 -0
- package/dist/wirings/scheduler/index.d.ts +1 -1
- package/dist/wirings/trigger/index.d.ts +1 -1
- package/dist/wirings/virtual-user/index.d.ts +5 -6
- package/dist/wirings/virtual-user/index.js +2 -4
- package/dist/wirings/workflow/dsl/workflow-dsl.types.d.ts +85 -15
- package/dist/wirings/workflow/feature.d.ts +2 -1
- package/dist/wirings/workflow/index.d.ts +5 -16
- package/dist/wirings/workflow/index.js +1 -9
- package/dist/wirings/workflow/pikku-scenario-service.d.ts +17 -7
- package/dist/wirings/workflow/pikku-scenario-service.js +48 -13
- package/dist/wirings/workflow/pikku-workflow-service.js +17 -3
- package/dist/wirings/workflow/scenario-step.types.d.ts +8 -0
- package/dist/wirings/workflow/scenario.types.d.ts +37 -0
- package/dist/wirings/workflow/workflow-approval-audit.d.ts +16 -0
- package/dist/wirings/workflow/workflow-approval-audit.js +40 -0
- package/dist/wirings/workflow/workflow-approval-policy.d.ts +20 -0
- package/dist/wirings/workflow/workflow-approval-policy.js +48 -0
- package/dist/wirings/workflow/workflow-approval.d.ts +29 -1
- package/dist/wirings/workflow/workflow-approval.js +65 -2
- package/dist/wirings/workflow/workflow-run-ownership.d.ts +2 -1
- package/dist/wirings/workflow/workflow-run-ownership.js +2 -1
- package/dist/wirings/workflow/workflow.types.d.ts +2 -37
- package/knowledge/decisions/internals/addon-pikku-meta-ships-at-the-package-root-or-under-dist.md +32 -0
- package/knowledge/decisions/internals/an-addon-scope-root-loses-to-a-root-the-host-already-declares.md +39 -0
- package/knowledge/decisions/internals/index.md +30 -3
- package/knowledge/decisions/internals/validate-runs-checks-by-precondition.md +115 -0
- package/knowledge/decisions/security/a-function-never-receives-the-secret-service.md +37 -0
- package/knowledge/decisions/security/a-workflow-run-is-read-and-approved-by-its-owner.md +30 -14
- package/knowledge/decisions/security/an-approval-answer-outlives-the-run-it-answered.md +59 -0
- package/knowledge/decisions/security/index.md +3 -1
- package/knowledge/questions/index.md +1 -1
- package/package.json +3 -2
- package/scripts/generate-api-report.mts +143 -18
- package/src/api-report.test.ts +2 -2
- package/src/errors/index.ts +1 -1
- package/src/function/function-runner.test.ts +52 -0
- package/src/function/function-runner.ts +5 -9
- package/src/function/index.ts +0 -2
- package/src/index.ts +0 -35
- package/src/pikku-state.ts +5 -0
- package/src/public-surface.json +81 -118
- package/src/services/ai-agent-runner-service.ts +12 -1
- package/src/services/ai-run-state-service.ts +11 -0
- package/src/services/in-memory-ai-run-state-service.ts +13 -0
- package/src/services/index.ts +7 -58
- package/src/services/meta-service.ts +2 -4
- package/src/services/scoped-credential-service.test.ts +86 -0
- package/src/services/scoped-credential-service.ts +63 -0
- package/src/testing/service-tests/ai-storage-service-tests.ts +93 -0
- package/src/types/core.types.ts +4 -7
- package/src/types/state.types.ts +21 -1
- package/src/wirings/actor-flow/index.ts +0 -3
- package/src/wirings/ai-agent/ai-agent-finalize.test.ts +186 -0
- package/src/wirings/ai-agent/ai-agent-finalize.ts +197 -0
- package/src/wirings/ai-agent/ai-agent-interrupt.ts +1 -0
- package/src/wirings/ai-agent/ai-agent-memory.ts +54 -38
- package/src/wirings/ai-agent/ai-agent-model-config.test.ts +72 -3
- package/src/wirings/ai-agent/ai-agent-model-config.ts +49 -1
- package/src/wirings/ai-agent/ai-agent-prepare.ts +4 -0
- package/src/wirings/ai-agent/ai-agent-runner.ts +71 -40
- package/src/wirings/ai-agent/ai-agent-stream-output-hooks.test.ts +353 -0
- package/src/wirings/ai-agent/ai-agent-stream.ts +116 -54
- package/src/wirings/ai-agent/ai-agent-turn.test.ts +67 -0
- package/src/wirings/ai-agent/ai-agent-turn.ts +1 -0
- package/src/wirings/ai-agent/ai-agent.types.ts +64 -4
- package/src/wirings/ai-agent/index.ts +2 -16
- package/src/wirings/ai-scorer/ai-scorer-grade.test.ts +106 -0
- package/src/wirings/ai-scorer/ai-scorer-grade.ts +55 -0
- package/src/wirings/ai-scorer/ai-scorer-judge.test.ts +143 -0
- package/src/wirings/ai-scorer/ai-scorer-judge.ts +120 -0
- package/src/wirings/ai-scorer/ai-scorer-live.test.ts +174 -0
- package/src/wirings/ai-scorer/ai-scorer-live.ts +56 -0
- package/src/wirings/ai-scorer/ai-scorer-registry.ts +63 -0
- package/src/wirings/ai-scorer/ai-scorer-sampling.test.ts +34 -0
- package/src/wirings/ai-scorer/ai-scorer-sampling.ts +36 -0
- package/src/wirings/ai-scorer/ai-scorer-snapshots.test.ts +49 -0
- package/src/wirings/ai-scorer/ai-scorer-snapshots.ts +46 -0
- package/src/wirings/ai-scorer/ai-scorer-worker.test.ts +122 -0
- package/src/wirings/ai-scorer/ai-scorer-worker.ts +69 -0
- package/src/wirings/ai-scorer/ai-scorer.ts +76 -0
- package/src/wirings/ai-scorer/ai-scorer.types.ts +107 -0
- package/src/wirings/ai-scorer/index.ts +24 -0
- package/src/wirings/channel/index.ts +1 -20
- package/src/wirings/channel/local/local-channel-runner.test.ts +68 -0
- package/src/wirings/channel/local/local-channel-runner.ts +8 -1
- package/src/wirings/cli/channel/cli-raw-channel-runner.test.ts +23 -0
- package/src/wirings/cli/channel/cli-raw-channel-runner.ts +12 -1
- package/src/wirings/cli/channel/index.ts +0 -7
- package/src/wirings/cli/cli-runner.test.ts +68 -0
- package/src/wirings/cli/cli-runner.ts +18 -1
- package/src/wirings/credential/index.ts +0 -1
- package/src/wirings/gateway/index.ts +0 -3
- package/src/wirings/http/http-runner.test.ts +66 -0
- package/src/wirings/http/http-runner.ts +10 -2
- package/src/wirings/http/index.ts +1 -1
- package/src/wirings/mcp/index.ts +0 -1
- package/src/wirings/mcp/mcp-runner.test.ts +181 -0
- package/src/wirings/mcp/mcp-runner.ts +35 -5
- package/src/wirings/persona/index.ts +0 -8
- package/src/wirings/queue/index.ts +0 -14
- package/src/wirings/rpc/addon-runner.ts +62 -3
- package/src/wirings/rpc/addon-secrets.test.ts +391 -0
- package/src/wirings/rpc/rpc-runner.test.ts +2 -0
- package/src/wirings/rpc/rpc-runner.ts +4 -0
- package/src/wirings/rpc/rpc-types.ts +8 -0
- package/src/wirings/rpc/wire-addon.ts +33 -0
- package/src/wirings/scheduler/index.ts +0 -1
- package/src/wirings/trigger/index.ts +0 -1
- package/src/wirings/virtual-user/index.ts +0 -16
- package/src/wirings/workflow/dsl/workflow-dsl.types.ts +96 -16
- package/src/wirings/workflow/feature.ts +2 -5
- package/src/wirings/workflow/graph/graph-runner.test.ts +72 -0
- package/src/wirings/workflow/index.ts +2 -68
- package/src/wirings/workflow/pikku-scenario-service.ts +81 -16
- package/src/wirings/workflow/pikku-workflow-service.test.ts +13 -12
- package/src/wirings/workflow/pikku-workflow-service.ts +28 -4
- package/src/wirings/workflow/scenario-expectations.test.ts +75 -0
- package/src/wirings/workflow/scenario-hooks.test.ts +3 -2
- package/src/wirings/workflow/scenario-step.types.ts +8 -0
- package/src/wirings/workflow/scenario.types.ts +63 -0
- package/src/wirings/workflow/workflow-approval-audit.ts +47 -0
- package/src/wirings/workflow/workflow-approval-policy.test.ts +524 -0
- package/src/wirings/workflow/workflow-approval-policy.ts +68 -0
- package/src/wirings/workflow/workflow-approval.ts +113 -9
- package/src/wirings/workflow/workflow-run-authority.test.ts +12 -15
- package/src/wirings/workflow/workflow-run-ownership.ts +2 -1
- package/src/wirings/workflow/workflow.types.ts +1 -63
- package/src/wirings-stay-decoupled.test.ts +6 -2
- package/tsconfig.tsbuildinfo +1 -1
- package/dist/internal.d.ts +0 -3
- package/dist/internal.js +0 -2
- package/dist/middleware/timeout.d.ts +0 -9
- package/dist/middleware/timeout.js +0 -15
- package/dist/pikku-response.d.ts +0 -6
- package/dist/pikku-response.js +0 -6
- package/dist/services/gopass-secrets.d.ts +0 -15
- package/dist/services/gopass-secrets.js +0 -76
- package/dist/services/http-scenario-actors.d.ts +0 -75
- package/dist/services/http-scenario-actors.js +0 -195
- package/dist/services/http-user-flow-actors.d.ts +0 -67
- package/dist/services/http-user-flow-actors.js +0 -193
- package/dist/services/scenario-actors-service.d.ts +0 -127
- package/dist/services/scenario-actors-service.js +0 -40
- package/dist/services/user-flow-actors-service.d.ts +0 -39
- package/dist/wirings/credential/wire-credential.d.ts +0 -48
- package/dist/wirings/credential/wire-credential.js +0 -47
- package/dist/wirings/oauth2/oauth2-client.d.ts +0 -47
- package/dist/wirings/oauth2/oauth2-client.js +0 -263
- package/dist/wirings/oauth2/oauth2-routes.d.ts +0 -35
- package/dist/wirings/oauth2/oauth2-routes.js +0 -146
- package/dist/wirings/scope/wire-scope.d.ts +0 -33
- package/dist/wirings/scope/wire-scope.js +0 -32
- package/dist/wirings/workflow/dsl/index.d.ts +0 -5
- package/dist/wirings/workflow/dsl/index.js +0 -4
- package/dist/wirings/workflow/graph/index.d.ts +0 -5
- package/dist/wirings/workflow/graph/index.js +0 -4
- /package/dist/{services/user-flow-actors-service.js → wirings/workflow/scenario.types.js} +0 -0
package/dist/internal.d.ts
DELETED
package/dist/internal.js
DELETED
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
export declare const timeout: () => import("../types/core.types.js").CorePikkuMiddlewareFactory<{
|
|
2
|
-
timeout: number;
|
|
3
|
-
}, import("../types/core.types.js").CoreSingletonServices<{
|
|
4
|
-
logLevel?: import("../services/logger.js").LogLevel;
|
|
5
|
-
secrets?: {};
|
|
6
|
-
workflow?: import("../wirings/workflow/workflow.types.js").WorkflowServiceConfig;
|
|
7
|
-
webhook?: import("../services/webhook-service.js").WebhookServiceConfig;
|
|
8
|
-
postgres?: import("../types/core.types.js").PostgresConfig;
|
|
9
|
-
}>, import("../types/core.types.js").CoreUserSession>;
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { RequestTimeoutError } from '../errors/errors.js';
|
|
2
|
-
import { pikkuMiddleware, pikkuMiddlewareFactory } from '../types/core.types.js';
|
|
3
|
-
export const timeout = () => pikkuMiddlewareFactory(({ timeout }) => pikkuMiddleware(async (_services, _wire, next) => {
|
|
4
|
-
let timeoutId;
|
|
5
|
-
await Promise.race([
|
|
6
|
-
next(),
|
|
7
|
-
new Promise((_resolve, reject) => {
|
|
8
|
-
timeoutId = setTimeout(() => reject(new RequestTimeoutError()), timeout);
|
|
9
|
-
}),
|
|
10
|
-
]).finally(() => {
|
|
11
|
-
if (timeoutId !== undefined) {
|
|
12
|
-
clearTimeout(timeoutId);
|
|
13
|
-
}
|
|
14
|
-
});
|
|
15
|
-
}));
|
package/dist/pikku-response.d.ts
DELETED
package/dist/pikku-response.js
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import type { SecretService } from './secret-service.js';
|
|
2
|
-
/**
|
|
3
|
-
* Service for retrieving secrets from gopass.
|
|
4
|
-
* Requires gopass to be installed and configured on the system.
|
|
5
|
-
*/
|
|
6
|
-
export declare class GopassSecretService implements SecretService {
|
|
7
|
-
private prefix;
|
|
8
|
-
constructor(prefix?: string);
|
|
9
|
-
private getFullKey;
|
|
10
|
-
getSecret<T = string>(key: string): Promise<T>;
|
|
11
|
-
hasSecret(key: string): Promise<boolean>;
|
|
12
|
-
setSecret(key: string, value: unknown): Promise<void>;
|
|
13
|
-
deleteSecret(key: string): Promise<void>;
|
|
14
|
-
getSecrets<T extends Record<string, unknown> = Record<string, unknown>>(keys: (keyof T & string)[]): Promise<Partial<T>>;
|
|
15
|
-
}
|
|
@@ -1,76 +0,0 @@
|
|
|
1
|
-
import { execFileSync } from 'child_process';
|
|
2
|
-
/**
|
|
3
|
-
* Service for retrieving secrets from gopass.
|
|
4
|
-
* Requires gopass to be installed and configured on the system.
|
|
5
|
-
*/
|
|
6
|
-
export class GopassSecretService {
|
|
7
|
-
prefix;
|
|
8
|
-
constructor(prefix = '') {
|
|
9
|
-
this.prefix = prefix;
|
|
10
|
-
}
|
|
11
|
-
getFullKey(key) {
|
|
12
|
-
if (!/^[\w.\-/]+$/.test(key)) {
|
|
13
|
-
throw new Error(`Invalid secret key format: ${key}`);
|
|
14
|
-
}
|
|
15
|
-
return this.prefix ? `${this.prefix}${key}` : key;
|
|
16
|
-
}
|
|
17
|
-
async getSecret(key) {
|
|
18
|
-
const fullKey = this.getFullKey(key);
|
|
19
|
-
try {
|
|
20
|
-
const raw = execFileSync('gopass', ['show', '-o', fullKey], {
|
|
21
|
-
encoding: 'utf8',
|
|
22
|
-
}).trim();
|
|
23
|
-
try {
|
|
24
|
-
return JSON.parse(raw);
|
|
25
|
-
}
|
|
26
|
-
catch {
|
|
27
|
-
return raw;
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
catch (error) {
|
|
31
|
-
throw new Error(`Secret Not Found: ${key}`, { cause: error });
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
async hasSecret(key) {
|
|
35
|
-
const fullKey = this.getFullKey(key);
|
|
36
|
-
try {
|
|
37
|
-
execFileSync('gopass', ['show', '-o', fullKey], { encoding: 'utf8' });
|
|
38
|
-
return true;
|
|
39
|
-
}
|
|
40
|
-
catch {
|
|
41
|
-
return false;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
async setSecret(key, value) {
|
|
45
|
-
const fullKey = this.getFullKey(key);
|
|
46
|
-
const encoded = typeof value === 'string' ? value : JSON.stringify(value);
|
|
47
|
-
try {
|
|
48
|
-
execFileSync('gopass', ['insert', '-f', fullKey], {
|
|
49
|
-
encoding: 'utf8',
|
|
50
|
-
input: encoded,
|
|
51
|
-
stdio: ['pipe', 'pipe', 'pipe'],
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
catch (error) {
|
|
55
|
-
throw new Error(`Failed to set secret: ${key}`, { cause: error });
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
async deleteSecret(key) {
|
|
59
|
-
const fullKey = this.getFullKey(key);
|
|
60
|
-
try {
|
|
61
|
-
execFileSync('gopass', ['rm', '-f', fullKey], { encoding: 'utf8' });
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
// Ignore errors if secret doesn't exist
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
async getSecrets(keys) {
|
|
68
|
-
const results = await Promise.allSettled(keys.map((k) => this.getSecret(k)));
|
|
69
|
-
const out = {};
|
|
70
|
-
keys.forEach((key, i) => {
|
|
71
|
-
if (results[i].status === 'fulfilled')
|
|
72
|
-
out[key] = results[i].value;
|
|
73
|
-
});
|
|
74
|
-
return out;
|
|
75
|
-
}
|
|
76
|
-
}
|
|
@@ -1,75 +0,0 @@
|
|
|
1
|
-
import type { ScenarioActor, ScenarioActorConfig, ScenarioActors, ScenarioInvokeOptions, ScenarioHttpResponse } from './scenario-actors-service.js';
|
|
2
|
-
import type { ConverseOptions, ActorFlowVerdict } from '../wirings/actor-flow/actor-flow.types.js';
|
|
3
|
-
export interface HttpScenarioActorsConfig {
|
|
4
|
-
/**
|
|
5
|
-
* Base API URL of the target app, INCLUDING the HTTP prefix — e.g.
|
|
6
|
-
* `https://app.example.com/api` or `http://localhost:4000/api`. Actor
|
|
7
|
-
* sign-in is reached at `${apiUrl}${signInPath}` and exposed RPCs at
|
|
8
|
-
* `${apiUrl}${rpcPath}/:rpcName`.
|
|
9
|
-
*/
|
|
10
|
-
apiUrl: string;
|
|
11
|
-
/**
|
|
12
|
-
* The actor impersonation secret. Sign-in only ever works for user rows
|
|
13
|
-
* flagged `actor: true` — knowing the secret never impersonates real users.
|
|
14
|
-
*/
|
|
15
|
-
secret: string;
|
|
16
|
-
/** Actor name → config (usually from pikku.config.json's actor registry). */
|
|
17
|
-
actors: Record<string, ScenarioActorConfig>;
|
|
18
|
-
/** Sign-in path under apiUrl. Default: the actor plugin's `/auth/sign-in/actor`. */
|
|
19
|
-
signInPath?: string;
|
|
20
|
-
/** Exposed-RPC path prefix under apiUrl. Default `/rpc`. */
|
|
21
|
-
rpcPath?: string;
|
|
22
|
-
/**
|
|
23
|
-
* Default model the persona uses when `actor.converse(...)` is called without
|
|
24
|
-
* an explicit `model`. The persona's own turns/approvals/evaluation run
|
|
25
|
-
* in-process via the configured `aiAgentRunner`.
|
|
26
|
-
*/
|
|
27
|
-
model?: string;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Default HTTP-backed actor. Signs in lazily on first invoke via the Better
|
|
31
|
-
* Auth actor plugin (`POST /auth/sign-in/actor` with `{ email, secret }` —
|
|
32
|
-
* the plugin upserts the actor-flagged user row and mints a session whose
|
|
33
|
-
* `actor` flag flows into audits/analytics). Holds the session cookies for
|
|
34
|
-
* its lifetime; a 401 mid-run re-logs-in once (long health-check runs can
|
|
35
|
-
* outlive a session).
|
|
36
|
-
*/
|
|
37
|
-
export declare class HttpScenarioActor implements ScenarioActor {
|
|
38
|
-
readonly name: string;
|
|
39
|
-
private actorConfig;
|
|
40
|
-
private config;
|
|
41
|
-
private jar;
|
|
42
|
-
/**
|
|
43
|
-
* Whether `login()` has succeeded since the last time the session was
|
|
44
|
-
* dropped. The jar cannot answer this — a target may set a cookie before
|
|
45
|
-
* anyone signs in, and it would then look like a session that was never
|
|
46
|
-
* established.
|
|
47
|
-
*/
|
|
48
|
-
private signedIn;
|
|
49
|
-
constructor(name: string, actorConfig: ScenarioActorConfig, config: HttpScenarioActorsConfig);
|
|
50
|
-
get email(): string;
|
|
51
|
-
invoke(rpcName: string, data: unknown): Promise<unknown>;
|
|
52
|
-
invokeRaw(rpcName: string, data: unknown, options?: ScenarioInvokeOptions): Promise<ScenarioHttpResponse>;
|
|
53
|
-
converse(options: ConverseOptions): Promise<ActorFlowVerdict>;
|
|
54
|
-
/** Start/continue the target agent's run over HTTP as this actor. */
|
|
55
|
-
private agentRun;
|
|
56
|
-
/** Answer the target agent's pending approvals over HTTP and continue. */
|
|
57
|
-
private agentApprove;
|
|
58
|
-
/**
|
|
59
|
-
* POST an agent HTTP route (raw body, not RPC-wrapped). Signs in lazily: the
|
|
60
|
-
* first call goes out with whatever cookie we hold (none, for a no-auth
|
|
61
|
-
* agent), and only a 401 triggers `login()` + one retry. This lets an actor
|
|
62
|
-
* converse with a no-auth agent without any sign-in wiring, while still
|
|
63
|
-
* authenticating against agents that require a session.
|
|
64
|
-
*/
|
|
65
|
-
private postAgent;
|
|
66
|
-
private postRpc;
|
|
67
|
-
/** Drop the session, so the next call signs in again before it goes out. */
|
|
68
|
-
private signOut;
|
|
69
|
-
private login;
|
|
70
|
-
}
|
|
71
|
-
/**
|
|
72
|
-
* Build the injected `actors` service from the config registry: actor name →
|
|
73
|
-
* lazy HTTP actor. Wire the result as the `actors` singleton service.
|
|
74
|
-
*/
|
|
75
|
-
export declare function createHttpScenarioActors(config: HttpScenarioActorsConfig): ScenarioActors;
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
import { readScenarioHttpResponse } from './scenario-actors-service.js';
|
|
2
|
-
import { runConversation } from '../wirings/actor-flow/run-conversation.js';
|
|
3
|
-
import { createCookieJar, } from '../wirings/workflow/scenario-cookie-jar.js';
|
|
4
|
-
import { getSingletonServices } from '../pikku-state.js';
|
|
5
|
-
import { AIProviderNotConfiguredError } from '../errors/errors.js';
|
|
6
|
-
/**
|
|
7
|
-
* Default HTTP-backed actor. Signs in lazily on first invoke via the Better
|
|
8
|
-
* Auth actor plugin (`POST /auth/sign-in/actor` with `{ email, secret }` —
|
|
9
|
-
* the plugin upserts the actor-flagged user row and mints a session whose
|
|
10
|
-
* `actor` flag flows into audits/analytics). Holds the session cookies for
|
|
11
|
-
* its lifetime; a 401 mid-run re-logs-in once (long health-check runs can
|
|
12
|
-
* outlive a session).
|
|
13
|
-
*/
|
|
14
|
-
export class HttpScenarioActor {
|
|
15
|
-
name;
|
|
16
|
-
actorConfig;
|
|
17
|
-
config;
|
|
18
|
-
jar;
|
|
19
|
-
/**
|
|
20
|
-
* Whether `login()` has succeeded since the last time the session was
|
|
21
|
-
* dropped. The jar cannot answer this — a target may set a cookie before
|
|
22
|
-
* anyone signs in, and it would then look like a session that was never
|
|
23
|
-
* established.
|
|
24
|
-
*/
|
|
25
|
-
signedIn = false;
|
|
26
|
-
constructor(name, actorConfig, config) {
|
|
27
|
-
this.name = name;
|
|
28
|
-
this.actorConfig = actorConfig;
|
|
29
|
-
this.config = config;
|
|
30
|
-
this.jar = createCookieJar(config.apiUrl);
|
|
31
|
-
}
|
|
32
|
-
get email() {
|
|
33
|
-
return this.actorConfig.email;
|
|
34
|
-
}
|
|
35
|
-
async invoke(rpcName, data) {
|
|
36
|
-
const res = await this.invokeRaw(rpcName, data);
|
|
37
|
-
if (!res.ok) {
|
|
38
|
-
throw new Error(`[scenario] '${rpcName}' as '${this.name}' returned ${res.status}: ${res.serialized.slice(0, 300)}`);
|
|
39
|
-
}
|
|
40
|
-
return res.body;
|
|
41
|
-
}
|
|
42
|
-
async invokeRaw(rpcName, data, options) {
|
|
43
|
-
if (!this.signedIn) {
|
|
44
|
-
await this.login();
|
|
45
|
-
}
|
|
46
|
-
let res = await this.postRpc(rpcName, data, options?.headers);
|
|
47
|
-
if (res.status === 401) {
|
|
48
|
-
// Session expired mid-run — re-login once and retry.
|
|
49
|
-
this.signOut();
|
|
50
|
-
await this.login();
|
|
51
|
-
res = await this.postRpc(rpcName, data, options?.headers);
|
|
52
|
-
}
|
|
53
|
-
return readScenarioHttpResponse(res);
|
|
54
|
-
}
|
|
55
|
-
async converse(options) {
|
|
56
|
-
const { aiAgentRunner } = getSingletonServices();
|
|
57
|
-
if (!aiAgentRunner) {
|
|
58
|
-
throw new AIProviderNotConfiguredError();
|
|
59
|
-
}
|
|
60
|
-
const model = options.model ?? this.config.model;
|
|
61
|
-
if (!model) {
|
|
62
|
-
throw new Error(`[scenario] actor '${this.name}' converse needs a model — pass options.model or set 'model' on the actors service`);
|
|
63
|
-
}
|
|
64
|
-
const threadId = globalThis.crypto.randomUUID();
|
|
65
|
-
const resourceId = `actor:${this.name}`;
|
|
66
|
-
return runConversation({
|
|
67
|
-
actor: this.actorConfig,
|
|
68
|
-
actorName: this.actorConfig.name ?? this.name,
|
|
69
|
-
agentName: options.agent,
|
|
70
|
-
task: options.task,
|
|
71
|
-
evaluate: options.evaluate,
|
|
72
|
-
approvals: options.approvals,
|
|
73
|
-
model,
|
|
74
|
-
maxTurns: options.maxTurns,
|
|
75
|
-
llm: (params) => aiAgentRunner.run(params),
|
|
76
|
-
target: {
|
|
77
|
-
run: (message) => this.agentRun(options.agent, message, threadId, resourceId),
|
|
78
|
-
approve: (runId, decisions) => this.agentApprove(options.agent, runId, decisions),
|
|
79
|
-
},
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
/** Start/continue the target agent's run over HTTP as this actor. */
|
|
83
|
-
async agentRun(agentName, message, threadId, resourceId) {
|
|
84
|
-
const raw = await this.postAgent(`agent/${agentName}`, {
|
|
85
|
-
message,
|
|
86
|
-
threadId,
|
|
87
|
-
resourceId,
|
|
88
|
-
});
|
|
89
|
-
return normalizeAgentReply(raw);
|
|
90
|
-
}
|
|
91
|
-
/** Answer the target agent's pending approvals over HTTP and continue. */
|
|
92
|
-
async agentApprove(agentName, runId, decisions) {
|
|
93
|
-
const raw = await this.postAgent(`agent/${agentName}/approve`, {
|
|
94
|
-
runId,
|
|
95
|
-
approvals: decisions,
|
|
96
|
-
});
|
|
97
|
-
return normalizeAgentReply(raw);
|
|
98
|
-
}
|
|
99
|
-
/**
|
|
100
|
-
* POST an agent HTTP route (raw body, not RPC-wrapped). Signs in lazily: the
|
|
101
|
-
* first call goes out with whatever cookie we hold (none, for a no-auth
|
|
102
|
-
* agent), and only a 401 triggers `login()` + one retry. This lets an actor
|
|
103
|
-
* converse with a no-auth agent without any sign-in wiring, while still
|
|
104
|
-
* authenticating against agents that require a session.
|
|
105
|
-
*/
|
|
106
|
-
async postAgent(subPath, body) {
|
|
107
|
-
const rpcPath = this.config.rpcPath ?? '/rpc';
|
|
108
|
-
const url = `${this.config.apiUrl}${rpcPath}/${subPath}`;
|
|
109
|
-
const send = () => this.jar.fetch(url, {
|
|
110
|
-
method: 'POST',
|
|
111
|
-
headers: { 'content-type': 'application/json' },
|
|
112
|
-
body: JSON.stringify(body),
|
|
113
|
-
});
|
|
114
|
-
let res = await send();
|
|
115
|
-
if (res.status === 401) {
|
|
116
|
-
this.signOut();
|
|
117
|
-
await this.login();
|
|
118
|
-
res = await send();
|
|
119
|
-
}
|
|
120
|
-
if (!res.ok) {
|
|
121
|
-
const text = (await res.text().catch(() => '')).slice(0, 300);
|
|
122
|
-
throw new Error(`[scenario] agent call '${subPath}' as '${this.name}' returned ${res.status}: ${text}`);
|
|
123
|
-
}
|
|
124
|
-
if (res.status === 204)
|
|
125
|
-
return undefined;
|
|
126
|
-
const text = await res.text();
|
|
127
|
-
return text ? JSON.parse(text) : undefined;
|
|
128
|
-
}
|
|
129
|
-
async postRpc(rpcName, data, extraHeaders) {
|
|
130
|
-
const rpcPath = this.config.rpcPath ?? '/rpc';
|
|
131
|
-
return this.jar.fetch(`${this.config.apiUrl}${rpcPath}/${rpcName}`, {
|
|
132
|
-
method: 'POST',
|
|
133
|
-
headers: { 'content-type': 'application/json', ...extraHeaders },
|
|
134
|
-
body: JSON.stringify({ data }),
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
/** Drop the session, so the next call signs in again before it goes out. */
|
|
138
|
-
signOut() {
|
|
139
|
-
this.jar.clear();
|
|
140
|
-
this.signedIn = false;
|
|
141
|
-
}
|
|
142
|
-
async login() {
|
|
143
|
-
const signInPath = this.config.signInPath ?? '/auth/sign-in/actor';
|
|
144
|
-
const res = await this.jar.fetch(`${this.config.apiUrl}${signInPath}`, {
|
|
145
|
-
method: 'POST',
|
|
146
|
-
headers: { 'content-type': 'application/json' },
|
|
147
|
-
body: JSON.stringify({
|
|
148
|
-
email: this.actorConfig.email,
|
|
149
|
-
name: this.actorConfig.name ?? this.name,
|
|
150
|
-
secret: this.config.secret,
|
|
151
|
-
}),
|
|
152
|
-
});
|
|
153
|
-
if (!res.ok) {
|
|
154
|
-
const body = (await res.text().catch(() => '')).slice(0, 300);
|
|
155
|
-
throw new Error(`[scenario] actor sign-in failed for '${this.name}' (${res.status}): ${body}`);
|
|
156
|
-
}
|
|
157
|
-
// What proves a session was established is this response setting a cookie,
|
|
158
|
-
// not the jar being non-empty — the target may have set one earlier.
|
|
159
|
-
if (res.headers.getSetCookie().length === 0) {
|
|
160
|
-
throw new Error(`[scenario] actor sign-in for '${this.name}' returned no session cookie`);
|
|
161
|
-
}
|
|
162
|
-
this.signedIn = true;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
/** Normalize an agentRun/agentApprove HTTP response into a TargetAgentReply. */
|
|
166
|
-
function normalizeAgentReply(raw) {
|
|
167
|
-
const r = (raw ?? {});
|
|
168
|
-
const pending = Array.isArray(r.pendingApprovals)
|
|
169
|
-
? r.pendingApprovals.map((p) => ({
|
|
170
|
-
toolCallId: String(p.toolCallId),
|
|
171
|
-
toolName: String(p.toolName),
|
|
172
|
-
args: p.args,
|
|
173
|
-
reason: typeof p.reason === 'string' ? p.reason : undefined,
|
|
174
|
-
}))
|
|
175
|
-
: undefined;
|
|
176
|
-
return {
|
|
177
|
-
text: typeof r.text === 'string' ? r.text : '',
|
|
178
|
-
runId: typeof r.runId === 'string' ? r.runId : '',
|
|
179
|
-
status: r.status === 'completed' || r.status === 'suspended'
|
|
180
|
-
? r.status
|
|
181
|
-
: undefined,
|
|
182
|
-
pendingApprovals: pending,
|
|
183
|
-
};
|
|
184
|
-
}
|
|
185
|
-
/**
|
|
186
|
-
* Build the injected `actors` service from the config registry: actor name →
|
|
187
|
-
* lazy HTTP actor. Wire the result as the `actors` singleton service.
|
|
188
|
-
*/
|
|
189
|
-
export function createHttpScenarioActors(config) {
|
|
190
|
-
const actors = {};
|
|
191
|
-
for (const [name, actorConfig] of Object.entries(config.actors)) {
|
|
192
|
-
actors[name] = new HttpScenarioActor(name, actorConfig, config);
|
|
193
|
-
}
|
|
194
|
-
return actors;
|
|
195
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import type { UserFlowActor, UserFlowActorConfig, UserFlowActors } from './user-flow-actors-service.js';
|
|
2
|
-
import type { ConverseOptions, ActorFlowVerdict } from '../wirings/actor-flow/actor-flow.types.js';
|
|
3
|
-
export interface HttpUserFlowActorsConfig {
|
|
4
|
-
/**
|
|
5
|
-
* Base API URL of the target app, INCLUDING the HTTP prefix — e.g.
|
|
6
|
-
* `https://app.example.com/api` or `http://localhost:4000/api`. Actor
|
|
7
|
-
* sign-in is reached at `${apiUrl}${signInPath}` and exposed RPCs at
|
|
8
|
-
* `${apiUrl}${rpcPath}/:rpcName`.
|
|
9
|
-
*/
|
|
10
|
-
apiUrl: string;
|
|
11
|
-
/**
|
|
12
|
-
* The actor impersonation secret. Sign-in only ever works for user rows
|
|
13
|
-
* flagged `actor: true` — knowing the secret never impersonates real users.
|
|
14
|
-
*/
|
|
15
|
-
secret: string;
|
|
16
|
-
/** Actor name → config (usually from pikku.config.json's actor registry). */
|
|
17
|
-
actors: Record<string, UserFlowActorConfig>;
|
|
18
|
-
/** Sign-in path under apiUrl. Default: the actor plugin's `/auth/sign-in/actor`. */
|
|
19
|
-
signInPath?: string;
|
|
20
|
-
/** Exposed-RPC path prefix under apiUrl. Default `/rpc`. */
|
|
21
|
-
rpcPath?: string;
|
|
22
|
-
/**
|
|
23
|
-
* Default model the persona uses when `actor.converse(...)` is called without
|
|
24
|
-
* an explicit `model`. The persona's own turns/approvals/evaluation run
|
|
25
|
-
* in-process via the configured `aiAgentRunner`.
|
|
26
|
-
*/
|
|
27
|
-
model?: string;
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Default HTTP-backed actor. Signs in lazily on first invoke via the Better
|
|
31
|
-
* Auth actor plugin (`POST /auth/sign-in/actor` with `{ email, secret }` —
|
|
32
|
-
* the plugin upserts the actor-flagged user row and mints a session whose
|
|
33
|
-
* `actor` flag flows into audits/analytics). Holds the session cookies for
|
|
34
|
-
* its lifetime; a 401 mid-run re-logs-in once (long health-check runs can
|
|
35
|
-
* outlive a session).
|
|
36
|
-
*/
|
|
37
|
-
export declare class HttpUserFlowActor implements UserFlowActor {
|
|
38
|
-
readonly name: string;
|
|
39
|
-
private actorConfig;
|
|
40
|
-
private config;
|
|
41
|
-
private cookie;
|
|
42
|
-
private origin;
|
|
43
|
-
constructor(name: string, actorConfig: UserFlowActorConfig, config: HttpUserFlowActorsConfig);
|
|
44
|
-
get email(): string;
|
|
45
|
-
invoke(rpcName: string, data: unknown): Promise<unknown>;
|
|
46
|
-
converse(options: ConverseOptions): Promise<ActorFlowVerdict>;
|
|
47
|
-
/** Start/continue the target agent's run over HTTP as this actor. */
|
|
48
|
-
private agentRun;
|
|
49
|
-
/** Answer the target agent's pending approvals over HTTP and continue. */
|
|
50
|
-
private agentApprove;
|
|
51
|
-
/**
|
|
52
|
-
* POST an agent HTTP route (raw body, not RPC-wrapped). Signs in lazily: the
|
|
53
|
-
* first call goes out with whatever cookie we hold (none, for a no-auth
|
|
54
|
-
* agent), and only a 401 triggers `login()` + one retry. This lets an actor
|
|
55
|
-
* converse with a no-auth agent without any sign-in wiring, while still
|
|
56
|
-
* authenticating against agents that require a session.
|
|
57
|
-
*/
|
|
58
|
-
private postAgent;
|
|
59
|
-
private postRpc;
|
|
60
|
-
private readRpcResponse;
|
|
61
|
-
private login;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Build the injected `actors` service from the config registry: actor name →
|
|
65
|
-
* lazy HTTP actor. Wire the result as the `actors` singleton service.
|
|
66
|
-
*/
|
|
67
|
-
export declare function createHttpUserFlowActors(config: HttpUserFlowActorsConfig): UserFlowActors;
|
|
@@ -1,193 +0,0 @@
|
|
|
1
|
-
import { runConversation } from '../wirings/actor-flow/run-conversation.js';
|
|
2
|
-
import { getSingletonServices } from '../pikku-state.js';
|
|
3
|
-
import { AIProviderNotConfiguredError } from '../errors/errors.js';
|
|
4
|
-
/**
|
|
5
|
-
* Default HTTP-backed actor. Signs in lazily on first invoke via the Better
|
|
6
|
-
* Auth actor plugin (`POST /auth/sign-in/actor` with `{ email, secret }` —
|
|
7
|
-
* the plugin upserts the actor-flagged user row and mints a session whose
|
|
8
|
-
* `actor` flag flows into audits/analytics). Holds the session cookies for
|
|
9
|
-
* its lifetime; a 401 mid-run re-logs-in once (long health-check runs can
|
|
10
|
-
* outlive a session).
|
|
11
|
-
*/
|
|
12
|
-
export class HttpUserFlowActor {
|
|
13
|
-
name;
|
|
14
|
-
actorConfig;
|
|
15
|
-
config;
|
|
16
|
-
cookie = null;
|
|
17
|
-
origin;
|
|
18
|
-
constructor(name, actorConfig, config) {
|
|
19
|
-
this.name = name;
|
|
20
|
-
this.actorConfig = actorConfig;
|
|
21
|
-
this.config = config;
|
|
22
|
-
this.origin = new URL(config.apiUrl).origin;
|
|
23
|
-
}
|
|
24
|
-
get email() {
|
|
25
|
-
return this.actorConfig.email;
|
|
26
|
-
}
|
|
27
|
-
async invoke(rpcName, data) {
|
|
28
|
-
const cookie = this.cookie ?? (await this.login());
|
|
29
|
-
const res = await this.postRpc(rpcName, data, cookie);
|
|
30
|
-
if (res.status === 401) {
|
|
31
|
-
// Session expired mid-run — re-login once and retry.
|
|
32
|
-
this.cookie = null;
|
|
33
|
-
return this.readRpcResponse(rpcName, await this.postRpc(rpcName, data, await this.login()));
|
|
34
|
-
}
|
|
35
|
-
return this.readRpcResponse(rpcName, res);
|
|
36
|
-
}
|
|
37
|
-
async converse(options) {
|
|
38
|
-
const { aiAgentRunner } = getSingletonServices();
|
|
39
|
-
if (!aiAgentRunner) {
|
|
40
|
-
throw new AIProviderNotConfiguredError();
|
|
41
|
-
}
|
|
42
|
-
const model = options.model ?? this.config.model;
|
|
43
|
-
if (!model) {
|
|
44
|
-
throw new Error(`[user-flow] actor '${this.name}' converse needs a model — pass options.model or set 'model' on the actors service`);
|
|
45
|
-
}
|
|
46
|
-
const threadId = globalThis.crypto.randomUUID();
|
|
47
|
-
const resourceId = `actor:${this.name}`;
|
|
48
|
-
return runConversation({
|
|
49
|
-
persona: this.actorConfig,
|
|
50
|
-
personaName: this.actorConfig.name ?? this.name,
|
|
51
|
-
agentName: options.agent,
|
|
52
|
-
task: options.task,
|
|
53
|
-
evaluate: options.evaluate,
|
|
54
|
-
approvals: options.approvals,
|
|
55
|
-
model,
|
|
56
|
-
maxTurns: options.maxTurns,
|
|
57
|
-
llm: (params) => aiAgentRunner.run(params),
|
|
58
|
-
target: {
|
|
59
|
-
run: (message) => this.agentRun(options.agent, message, threadId, resourceId),
|
|
60
|
-
approve: (runId, decisions) => this.agentApprove(options.agent, runId, decisions),
|
|
61
|
-
},
|
|
62
|
-
});
|
|
63
|
-
}
|
|
64
|
-
/** Start/continue the target agent's run over HTTP as this actor. */
|
|
65
|
-
async agentRun(agentName, message, threadId, resourceId) {
|
|
66
|
-
const raw = await this.postAgent(`agent/${agentName}`, {
|
|
67
|
-
message,
|
|
68
|
-
threadId,
|
|
69
|
-
resourceId,
|
|
70
|
-
});
|
|
71
|
-
return normalizeAgentReply(raw);
|
|
72
|
-
}
|
|
73
|
-
/** Answer the target agent's pending approvals over HTTP and continue. */
|
|
74
|
-
async agentApprove(agentName, runId, decisions) {
|
|
75
|
-
const raw = await this.postAgent(`agent/${agentName}/approve`, {
|
|
76
|
-
runId,
|
|
77
|
-
approvals: decisions,
|
|
78
|
-
});
|
|
79
|
-
return normalizeAgentReply(raw);
|
|
80
|
-
}
|
|
81
|
-
/**
|
|
82
|
-
* POST an agent HTTP route (raw body, not RPC-wrapped). Signs in lazily: the
|
|
83
|
-
* first call goes out with whatever cookie we hold (none, for a no-auth
|
|
84
|
-
* agent), and only a 401 triggers `login()` + one retry. This lets an actor
|
|
85
|
-
* converse with a no-auth agent without any sign-in wiring, while still
|
|
86
|
-
* authenticating against agents that require a session.
|
|
87
|
-
*/
|
|
88
|
-
async postAgent(subPath, body) {
|
|
89
|
-
const rpcPath = this.config.rpcPath ?? '/rpc';
|
|
90
|
-
const url = `${this.config.apiUrl}${rpcPath}/${subPath}`;
|
|
91
|
-
const send = (cookie) => fetch(url, {
|
|
92
|
-
method: 'POST',
|
|
93
|
-
headers: {
|
|
94
|
-
'content-type': 'application/json',
|
|
95
|
-
origin: this.origin,
|
|
96
|
-
...(cookie ? { cookie } : {}),
|
|
97
|
-
},
|
|
98
|
-
body: JSON.stringify(body),
|
|
99
|
-
});
|
|
100
|
-
let res = await send(this.cookie);
|
|
101
|
-
if (res.status === 401) {
|
|
102
|
-
this.cookie = null;
|
|
103
|
-
res = await send(await this.login());
|
|
104
|
-
}
|
|
105
|
-
if (!res.ok) {
|
|
106
|
-
const text = (await res.text().catch(() => '')).slice(0, 300);
|
|
107
|
-
throw new Error(`[user-flow] agent call '${subPath}' as '${this.name}' returned ${res.status}: ${text}`);
|
|
108
|
-
}
|
|
109
|
-
if (res.status === 204)
|
|
110
|
-
return undefined;
|
|
111
|
-
const text = await res.text();
|
|
112
|
-
return text ? JSON.parse(text) : undefined;
|
|
113
|
-
}
|
|
114
|
-
async postRpc(rpcName, data, cookie) {
|
|
115
|
-
const rpcPath = this.config.rpcPath ?? '/rpc';
|
|
116
|
-
return fetch(`${this.config.apiUrl}${rpcPath}/${rpcName}`, {
|
|
117
|
-
method: 'POST',
|
|
118
|
-
headers: {
|
|
119
|
-
'content-type': 'application/json',
|
|
120
|
-
origin: this.origin,
|
|
121
|
-
cookie,
|
|
122
|
-
},
|
|
123
|
-
body: JSON.stringify({ data }),
|
|
124
|
-
});
|
|
125
|
-
}
|
|
126
|
-
async readRpcResponse(rpcName, res) {
|
|
127
|
-
if (!res.ok) {
|
|
128
|
-
const body = (await res.text().catch(() => '')).slice(0, 300);
|
|
129
|
-
throw new Error(`[user-flow] '${rpcName}' as '${this.name}' returned ${res.status}: ${body}`);
|
|
130
|
-
}
|
|
131
|
-
if (res.status === 204)
|
|
132
|
-
return undefined;
|
|
133
|
-
const text = await res.text();
|
|
134
|
-
return text ? JSON.parse(text) : undefined;
|
|
135
|
-
}
|
|
136
|
-
async login() {
|
|
137
|
-
const signInPath = this.config.signInPath ?? '/auth/sign-in/actor';
|
|
138
|
-
const res = await fetch(`${this.config.apiUrl}${signInPath}`, {
|
|
139
|
-
method: 'POST',
|
|
140
|
-
headers: { 'content-type': 'application/json', origin: this.origin },
|
|
141
|
-
body: JSON.stringify({
|
|
142
|
-
email: this.actorConfig.email,
|
|
143
|
-
name: this.actorConfig.name ?? this.name,
|
|
144
|
-
secret: this.config.secret,
|
|
145
|
-
}),
|
|
146
|
-
});
|
|
147
|
-
if (!res.ok) {
|
|
148
|
-
const body = (await res.text().catch(() => '')).slice(0, 300);
|
|
149
|
-
throw new Error(`[user-flow] actor sign-in failed for '${this.name}' (${res.status}): ${body}`);
|
|
150
|
-
}
|
|
151
|
-
const setCookies = res.headers.getSetCookie?.() ?? [];
|
|
152
|
-
const cookie = setCookies
|
|
153
|
-
.map((c) => c.split(';')[0])
|
|
154
|
-
.filter(Boolean)
|
|
155
|
-
.join('; ');
|
|
156
|
-
if (!cookie) {
|
|
157
|
-
throw new Error(`[user-flow] actor sign-in for '${this.name}' returned no session cookie`);
|
|
158
|
-
}
|
|
159
|
-
this.cookie = cookie;
|
|
160
|
-
return cookie;
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
/** Normalize an agentRun/agentApprove HTTP response into a TargetAgentReply. */
|
|
164
|
-
function normalizeAgentReply(raw) {
|
|
165
|
-
const r = (raw ?? {});
|
|
166
|
-
const pending = Array.isArray(r.pendingApprovals)
|
|
167
|
-
? r.pendingApprovals.map((p) => ({
|
|
168
|
-
toolCallId: String(p.toolCallId),
|
|
169
|
-
toolName: String(p.toolName),
|
|
170
|
-
args: p.args,
|
|
171
|
-
reason: typeof p.reason === 'string' ? p.reason : undefined,
|
|
172
|
-
}))
|
|
173
|
-
: undefined;
|
|
174
|
-
return {
|
|
175
|
-
text: typeof r.text === 'string' ? r.text : '',
|
|
176
|
-
runId: typeof r.runId === 'string' ? r.runId : '',
|
|
177
|
-
status: r.status === 'completed' || r.status === 'suspended'
|
|
178
|
-
? r.status
|
|
179
|
-
: undefined,
|
|
180
|
-
pendingApprovals: pending,
|
|
181
|
-
};
|
|
182
|
-
}
|
|
183
|
-
/**
|
|
184
|
-
* Build the injected `actors` service from the config registry: actor name →
|
|
185
|
-
* lazy HTTP actor. Wire the result as the `actors` singleton service.
|
|
186
|
-
*/
|
|
187
|
-
export function createHttpUserFlowActors(config) {
|
|
188
|
-
const actors = {};
|
|
189
|
-
for (const [name, actorConfig] of Object.entries(config.actors)) {
|
|
190
|
-
actors[name] = new HttpUserFlowActor(name, actorConfig, config);
|
|
191
|
-
}
|
|
192
|
-
return actors;
|
|
193
|
-
}
|