@superpanel-sdk/runtime 0.5.0 → 0.7.0
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/README.md +3 -3
- package/dist/builder/codegen/emit.d.ts +5 -10
- package/dist/builder/codegen/emit.js +100 -106
- package/dist/builder/codegen/generate.d.ts +3 -5
- package/dist/builder/codegen/generate.js +13 -32
- package/dist/builder/company-imports.d.ts +5 -0
- package/dist/builder/company-imports.js +9 -1
- package/dist/builder/compile/compile-sequence.d.ts +13 -11
- package/dist/builder/compile/compile-sequence.js +135 -83
- package/dist/builder/compile/function-code.d.ts +15 -9
- package/dist/builder/compile/function-code.js +59 -29
- package/dist/builder/compile/registry.d.ts +4 -4
- package/dist/builder/compile/registry.js +14 -12
- package/dist/builder/errors.d.ts +9 -0
- package/dist/builder/errors.js +17 -0
- package/dist/builder/function-params.d.ts +10 -0
- package/dist/builder/function-params.js +203 -0
- package/dist/builder/load.d.ts +17 -17
- package/dist/builder/load.js +27 -25
- package/dist/builder/paths.d.ts +6 -1
- package/dist/builder/paths.js +16 -2
- package/dist/builder/schedules.d.ts +9 -7
- package/dist/builder/schedules.js +20 -21
- package/dist/builder/typecheck.d.ts +1 -1
- package/dist/builder/typecheck.js +3 -6
- package/dist/builder/types.d.ts +41 -5
- package/dist/builder/types.js +19 -0
- package/dist/cli/calls/index.d.ts +177 -0
- package/dist/cli/calls/index.js +96 -0
- package/dist/cli/debug/index.d.ts +31 -0
- package/dist/cli/debug/index.js +119 -0
- package/dist/cli/exec/index.d.ts +39 -0
- package/dist/cli/exec/index.js +138 -0
- package/dist/cli/exec/session.d.ts +48 -0
- package/dist/cli/exec/session.js +82 -0
- package/dist/cli/exec/trace.d.ts +18 -0
- package/dist/cli/exec/trace.js +52 -0
- package/dist/cli/index.js +84 -1
- package/dist/cli/logs/index.d.ts +47 -0
- package/dist/cli/logs/index.js +113 -0
- package/dist/cli/mobile/index.d.ts +12 -0
- package/dist/cli/mobile/index.js +405 -0
- package/dist/cli/serve/index.d.ts +40 -4
- package/dist/cli/serve/index.js +167 -28
- package/dist/cli/turns/index.d.ts +105 -0
- package/dist/cli/turns/index.js +128 -0
- package/dist/cli/turns/run.d.ts +13 -0
- package/dist/cli/turns/run.js +57 -0
- package/dist/cli/turns/shadow.d.ts +112 -0
- package/dist/cli/turns/shadow.js +54 -0
- package/dist/cli/type-gen/index.js +0 -1
- package/dist/compute-host/compute-host.d.ts +5 -6
- package/dist/compute-host/compute-host.js +4 -13
- package/dist/compute-host/source-compute-host.d.ts +5 -7
- package/dist/compute-host/source-compute-host.js +12 -33
- package/dist/core/case-orchestrator/contact-hours.d.ts +26 -6
- package/dist/core/case-orchestrator/contact-hours.js +27 -8
- package/dist/core/case-orchestrator/index.d.ts +1 -4
- package/dist/core/case-orchestrator/index.js +1 -4
- package/dist/core/case-orchestrator/render.d.ts +1 -1
- package/dist/core/case-orchestrator/render.js +15 -10
- package/dist/core/case-orchestrator/types.d.ts +83 -128
- package/dist/core/case-orchestrator/types.js +0 -4
- package/dist/core/context/email-message.d.ts +40 -1
- package/dist/core/context/email-message.js +27 -0
- package/dist/core/context/email.d.ts +13 -1
- package/dist/core/context/email.js +2 -3
- package/dist/core/context/exchanges.d.ts +25 -0
- package/dist/core/context/exchanges.js +25 -7
- package/dist/core/context/instances.d.ts +31 -2
- package/dist/core/context/instances.js +18 -4
- package/dist/core/context/platform-registry.d.ts +35 -0
- package/dist/core/context/sms.d.ts +30 -0
- package/dist/core/context/sms.js +20 -0
- package/dist/core/context/types.d.ts +29 -1
- package/dist/core/conversation/index.d.ts +58 -4
- package/dist/core/conversation/index.js +123 -4
- package/dist/core/functions/errors.d.ts +74 -0
- package/dist/core/functions/errors.js +26 -0
- package/dist/core/functions/events.d.ts +23 -78
- package/dist/core/functions/events.js +8 -17
- package/dist/core/functions/schedules.d.ts +9 -10
- package/dist/core/functions/schedules.js +2 -2
- package/dist/core/functions/types.d.ts +17 -72
- package/dist/core/functions/types.js +1 -9
- package/dist/core/page/define.d.ts +230 -160
- package/dist/core/page/define.js +145 -104
- package/dist/core/page/envelope.d.ts +55 -0
- package/dist/core/page/envelope.js +4 -0
- package/dist/core/page/fns.d.ts +448 -0
- package/dist/core/page/fns.js +539 -0
- package/dist/core/page/memory-reader.d.ts +27 -0
- package/dist/core/page/memory-reader.js +299 -0
- package/dist/core/page/page-host.d.ts +20 -0
- package/dist/core/page/page-host.js +57 -0
- package/dist/core/page/records-query.d.ts +234 -0
- package/dist/core/page/records-query.js +780 -0
- package/dist/core/page/render.d.ts +67 -25
- package/dist/core/page/render.js +492 -272
- package/dist/core/page/sandbox.d.ts +85 -0
- package/dist/core/page/sandbox.js +357 -0
- package/dist/core/page/time-format.d.ts +27 -0
- package/dist/core/page/time-format.js +84 -0
- package/dist/core/page/typed.d.ts +165 -159
- package/dist/core/page/workspace.d.ts +25 -1
- package/dist/core/page/workspace.js +48 -13
- package/dist/core/party/schema.d.ts +74 -3
- package/dist/core/party/schema.js +39 -6
- package/dist/core/party/types.d.ts +8 -13
- package/dist/core/sequence/define.d.ts +1 -2
- package/dist/core/slot/company-metadata.d.ts +11 -22
- package/dist/core/slot/company-metadata.js +12 -39
- package/dist/core/slot/compile-schema.js +6 -6
- package/dist/core/slot/field.d.ts +1 -2
- package/dist/core/slot/field.js +0 -2
- package/dist/core/slot/index.d.ts +3 -3
- package/dist/core/slot/index.js +3 -3
- package/dist/core/slot/naming.d.ts +1 -1
- package/dist/core/slot/naming.js +1 -1
- package/dist/core/slot/platform-fields.d.ts +4 -4
- package/dist/core/slot/platform-fields.js +5 -5
- package/dist/core/slot/schema-compat.d.ts +8 -7
- package/dist/core/slot/schema-compat.js +22 -31
- package/dist/core/slot/schema.d.ts +3 -59
- package/dist/core/slot/schema.js +0 -15
- package/dist/core/slot/types.d.ts +4 -10
- package/dist/core/slot/value.d.ts +2 -0
- package/dist/core/slot/value.js +6 -2
- package/dist/dev-overlay/protocol.d.ts +38 -5
- package/dist/fn-context/build-context.js +10 -4
- package/dist/fn-context/fake/index.d.ts +2 -0
- package/dist/fn-context/fake/index.js +5 -3
- package/dist/fn-context/fn-params.d.ts +2 -5
- package/dist/fn-context/fn-params.js +6 -9
- package/dist/fn-context/index.d.ts +1 -1
- package/dist/fn-context/make-ctx.js +5 -4
- package/dist/fn-context/rpc/case-orchestrator-tools.d.ts +11 -3
- package/dist/fn-context/rpc/case-orchestrator-tools.js +0 -1
- package/dist/fn-context/rpc/errors.d.ts +13 -0
- package/dist/fn-context/rpc/errors.js +22 -0
- package/dist/fn-context/rpc/index.d.ts +1 -1
- package/dist/fn-context/rpc/index.js +1 -1
- package/dist/fn-context/rpc/integration/index.d.ts +11 -1
- package/dist/fn-context/rpc/log.d.ts +11 -1
- package/dist/fn-context/rpc/platform.d.ts +11 -1
- package/dist/fn-context/rpc/records.d.ts +13 -6
- package/dist/fn-context/rpc/records.js +2 -4
- package/dist/fn-context/rpc/trpc-client.d.ts +42 -0
- package/dist/fn-context/rpc/trpc-client.js +151 -22
- package/dist/fn-context/rpc/trpc-init.d.ts +19 -1
- package/dist/fn-context/rpc/trpc-init.js +10 -4
- package/dist/fn-context/rpc/trpc-router-factory.d.ts +66 -8
- package/dist/fn-context/run-scope.js +1 -1
- package/dist/fn-context/types.d.ts +20 -6
- package/dist/index.d.ts +11 -12
- package/dist/index.js +5 -8
- package/dist/logger.d.ts +4 -0
- package/dist/logger.js +8 -0
- package/dist/platform.d.ts +32 -33
- package/dist/platform.js +23 -22
- package/dist/testing-compile-child.d.ts +2 -0
- package/dist/testing-compile-child.js +20 -0
- package/dist/testing.d.ts +368 -0
- package/dist/testing.js +439 -0
- package/dist/voice-service/actions.d.ts +12 -35
- package/dist/voice-service/index.d.ts +2 -0
- package/dist/voice-service/index.js +2 -0
- package/dist/voice-service/protocol.d.ts +604 -0
- package/dist/voice-service/protocol.js +67 -0
- package/dist/voice-service/types.d.ts +16 -6
- package/dist/worker.d.ts +2 -1
- package/dist/worker.js +2 -1
- package/package.json +20 -16
- package/dist/core/case-orchestrator/follow-ups.d.ts +0 -31
- package/dist/core/case-orchestrator/follow-ups.js +0 -103
- package/dist/core/case-orchestrator/tools.d.ts +0 -29
- package/dist/core/case-orchestrator/tools.js +0 -31
- package/dist/core/case-orchestrator/validate.d.ts +0 -11
- package/dist/core/case-orchestrator/validate.js +0 -63
- package/dist/core/functions/define/bind.d.ts +0 -25
- package/dist/core/functions/define/bind.js +0 -14
- package/dist/core/functions/define/case.d.ts +0 -9
- package/dist/core/functions/define/case.js +0 -13
- package/dist/core/functions/define/events.d.ts +0 -20
- package/dist/core/functions/define/events.js +0 -21
- package/dist/core/functions/define/no-case.d.ts +0 -4
- package/dist/core/functions/define/no-case.js +0 -9
- package/dist/core/functions/func-ref.d.ts +0 -25
- package/dist/core/functions/func-ref.js +0 -20
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import type { TextCallEvent } from '../calls/index.js';
|
|
2
|
+
import type { TurnSummary } from './index.js';
|
|
3
|
+
export type TurnInput = {
|
|
4
|
+
/** The case; left out with `from` (the turn's). */
|
|
5
|
+
instanceId?: string;
|
|
6
|
+
channel?: 'sms' | 'email' | 'call';
|
|
7
|
+
/** What the customer writes or says; with `from`, in place of what they said then. */
|
|
8
|
+
say?: string;
|
|
9
|
+
/** The conversation's contact; the case's subject when it is a contact and none is named. */
|
|
10
|
+
caller?: string;
|
|
11
|
+
/** The conversation so far, by hand, oldest first; the case's real one when left out. */
|
|
12
|
+
history?: Array<{
|
|
13
|
+
customer?: string;
|
|
14
|
+
ai?: string;
|
|
15
|
+
}>;
|
|
16
|
+
/** Start where a turn started: its id, or the directory it was exported to (relative to the repository). */
|
|
17
|
+
from?: string;
|
|
18
|
+
/** Run it this many times (1–10), each from the same start: the model answers differently each time. */
|
|
19
|
+
repeat?: number;
|
|
20
|
+
/** Results for the company's functions the turn presses, by name; a recorded call (`from`) is replayed first. */
|
|
21
|
+
functions?: {
|
|
22
|
+
results?: Record<string, unknown>;
|
|
23
|
+
};
|
|
24
|
+
};
|
|
25
|
+
/** One run of the turn. */
|
|
26
|
+
export type ShadowRun = {
|
|
27
|
+
runId: string | null;
|
|
28
|
+
/** A text or email run: how it ended (`reply` with its `text`, not sent). A call's turn: null. */
|
|
29
|
+
outcome: {
|
|
30
|
+
kind: 'reply';
|
|
31
|
+
text: string;
|
|
32
|
+
email?: {
|
|
33
|
+
subject?: string;
|
|
34
|
+
cc?: string[];
|
|
35
|
+
};
|
|
36
|
+
steps: number;
|
|
37
|
+
} | {
|
|
38
|
+
kind: 'noReply' | 'handoff' | 'abnormal';
|
|
39
|
+
reason: string;
|
|
40
|
+
steps: number;
|
|
41
|
+
} | null;
|
|
42
|
+
/** What the AI said, or would have sent. */
|
|
43
|
+
said: string[];
|
|
44
|
+
/** `act`: what the platform told the model after running the actions. */
|
|
45
|
+
stepResult?: string;
|
|
46
|
+
actions: Array<{
|
|
47
|
+
name: string;
|
|
48
|
+
kind: string;
|
|
49
|
+
args: unknown;
|
|
50
|
+
status: string;
|
|
51
|
+
error: string | null;
|
|
52
|
+
result: unknown;
|
|
53
|
+
}>;
|
|
54
|
+
/** Each field it wrote: its row, table and field, the value and state it took, and what it was. */
|
|
55
|
+
writes: Array<{
|
|
56
|
+
rowId: string;
|
|
57
|
+
table: string;
|
|
58
|
+
field: string;
|
|
59
|
+
value: unknown;
|
|
60
|
+
state: string | null;
|
|
61
|
+
from: {
|
|
62
|
+
value: unknown;
|
|
63
|
+
state: string | null;
|
|
64
|
+
};
|
|
65
|
+
reason: string | null;
|
|
66
|
+
}>;
|
|
67
|
+
/** A call: what it did on the line (a keypad, a transfer, a voicemail). */
|
|
68
|
+
events: TextCallEvent[];
|
|
69
|
+
/** A call the AI ended in this turn. */
|
|
70
|
+
ended: boolean;
|
|
71
|
+
turns: TurnSummary[];
|
|
72
|
+
/** Each company function the turn pressed: `replayed` (as it ran then), `given` (yours), `unrecorded` (not run), `skipped` (an event handler). */
|
|
73
|
+
functions: Array<{
|
|
74
|
+
name: string;
|
|
75
|
+
args: unknown;
|
|
76
|
+
how: 'replayed' | 'given' | 'unrecorded' | 'skipped';
|
|
77
|
+
result?: unknown;
|
|
78
|
+
error?: string;
|
|
79
|
+
}>;
|
|
80
|
+
/** What it would have done outside the database (a text, an email, an Orchestrator run): none of it happened. */
|
|
81
|
+
held: string[];
|
|
82
|
+
/** Where it could not start exactly as asked (a row gone since). */
|
|
83
|
+
warnings: string[];
|
|
84
|
+
/** Whether it pressed the button with this run key (or name). */
|
|
85
|
+
clicked(key: string): boolean;
|
|
86
|
+
/** Whether it wrote `table.field` (to `value`, when given). */
|
|
87
|
+
wrote(field: string, value?: unknown): boolean;
|
|
88
|
+
};
|
|
89
|
+
/** What a `turn` or an `act` did: its first run's, and every run with `repeat`. */
|
|
90
|
+
export type RunResult = ShadowRun & {
|
|
91
|
+
runs: ShadowRun[];
|
|
92
|
+
/** The turn it started from. */
|
|
93
|
+
from: {
|
|
94
|
+
turnId: string;
|
|
95
|
+
kind: string;
|
|
96
|
+
heard: string | null;
|
|
97
|
+
} | null;
|
|
98
|
+
/** The share of runs `check` holds for, 0–1. */
|
|
99
|
+
rate(check: (run: ShadowRun) => boolean): number;
|
|
100
|
+
};
|
|
101
|
+
type Post = <T>(what: string, body: unknown) => Promise<T>;
|
|
102
|
+
/** `from` as the backend takes it: a turn id as it is, a directory's files read. */
|
|
103
|
+
export declare function fromOf(repoRoot: string, from: string): string | {
|
|
104
|
+
turn: unknown;
|
|
105
|
+
history: unknown;
|
|
106
|
+
data: unknown;
|
|
107
|
+
};
|
|
108
|
+
export declare function shadowTurn(post: Post, repoRoot: string, what: 'turn' | 'act', input: TurnInput & {
|
|
109
|
+
actions?: unknown[];
|
|
110
|
+
}): Promise<RunResult>;
|
|
111
|
+
export {};
|
|
112
|
+
//# sourceMappingURL=shadow.d.ts.map
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A single turn, always a shadow (68 §8): `sandbox.turn` / `sandbox.act` and `superpanel-sdk-runtime turn`.
|
|
3
|
+
* The backend runs it on a shadow of the environment's database, rolled back afterwards; what it did comes
|
|
4
|
+
* back. `from` names a turn by id, or the directory `turns export` wrote it to (kept in the repository,
|
|
5
|
+
* it outlives the 30 days a turn's snapshot is kept): its files are sent as they are.
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync, readFileSync, statSync } from 'node:fs';
|
|
8
|
+
import path from 'node:path';
|
|
9
|
+
/** `from` as the backend takes it: a turn id as it is, a directory's files read. */
|
|
10
|
+
export function fromOf(repoRoot, from) {
|
|
11
|
+
const dir = path.resolve(repoRoot, from);
|
|
12
|
+
const isDir = (() => {
|
|
13
|
+
try {
|
|
14
|
+
return statSync(dir).isDirectory();
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
})();
|
|
20
|
+
if (!isDir) {
|
|
21
|
+
if (/[\\/]/.test(from))
|
|
22
|
+
throw new Error(`turn: from ${from}: no such directory`);
|
|
23
|
+
return from;
|
|
24
|
+
}
|
|
25
|
+
const json = (name, required) => {
|
|
26
|
+
const file = path.join(dir, name);
|
|
27
|
+
if (!existsSync(file)) {
|
|
28
|
+
if (required)
|
|
29
|
+
throw new Error(`turn: ${from} has no ${name} (export a turn with \`turns export <turnId>\`)`);
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
return JSON.parse(readFileSync(file, 'utf8'));
|
|
33
|
+
};
|
|
34
|
+
return { turn: json('turn.json', true), history: json('history.json', true), data: json('data.json', false) ?? {} };
|
|
35
|
+
}
|
|
36
|
+
function withChecks(run) {
|
|
37
|
+
return {
|
|
38
|
+
...run,
|
|
39
|
+
clicked: (key) => run.actions.some((a) => a.status !== 'failed' && (a.name === key || run.turns.some((t) => t.actions.some((ta) => ta.key === key && ta.status !== 'failed')))),
|
|
40
|
+
wrote: (field, ...value) => run.writes.some((w) => `${w.table}.${w.field}` === field && (value.length === 0 || JSON.stringify(w.value) === JSON.stringify(value[0]))),
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
export async function shadowTurn(post, repoRoot, what, input) {
|
|
44
|
+
const body = { ...input, ...(input.from ? { from: fromOf(repoRoot, input.from) } : {}) };
|
|
45
|
+
const wire = await post(what, body);
|
|
46
|
+
const runs = wire.runs.map(withChecks);
|
|
47
|
+
return {
|
|
48
|
+
...runs[0],
|
|
49
|
+
runs,
|
|
50
|
+
from: wire.from,
|
|
51
|
+
rate: (check) => (runs.length ? runs.filter(check).length / runs.length : 0),
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=shadow.js.map
|
|
@@ -9,7 +9,6 @@ async function runOnce(repoRoot, sequenceIds) {
|
|
|
9
9
|
}
|
|
10
10
|
for (const result of results) {
|
|
11
11
|
console.log(`wrote ${path.relative(repoRoot, result.outPath)}`);
|
|
12
|
-
console.log(`wrote ${path.relative(repoRoot, result.caseContextOutPath)}`);
|
|
13
12
|
console.log(`wrote ${path.relative(repoRoot, result.indexOutPath)}`);
|
|
14
13
|
}
|
|
15
14
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Pluggable host for sequence functions (
|
|
2
|
+
* Pluggable host for sequence functions: every one is `(ctx, args)` (38 §2.1); the host builds `ctx`.
|
|
3
3
|
*
|
|
4
4
|
* - Local unit tests / company CI: {@link createLocalComputeHost} (direct JS call)
|
|
5
5
|
* - Author FuncMap: `createSourceComputeHost`
|
|
@@ -11,18 +11,17 @@ export interface ComputeHost {
|
|
|
11
11
|
/** Release host-owned resources. No-op when the host has nothing to free. */
|
|
12
12
|
dispose(): void;
|
|
13
13
|
}
|
|
14
|
-
/**
|
|
14
|
+
/** What a function is called with besides `ctx`: its `args`, one object (`{}` when left out). */
|
|
15
15
|
export type FunctionInvocation = {
|
|
16
|
-
|
|
17
|
-
params?: object;
|
|
16
|
+
args?: object;
|
|
18
17
|
};
|
|
19
18
|
export type ComputeInvocation = FunctionInvocation;
|
|
20
19
|
export declare function parseComputeInvocation(value: unknown): ComputeInvocation | null;
|
|
21
20
|
/**
|
|
22
|
-
* Name → plain function of `
|
|
21
|
+
* Name → plain function of `args` (local tests stand in sequence functions with these).
|
|
23
22
|
* Parameter typed as `never` so company-narrowed maps remain assignable (TS contravariance).
|
|
24
23
|
*/
|
|
25
|
-
export type ComputeFnMap = Record<string, (
|
|
24
|
+
export type ComputeFnMap = Record<string, (args: never) => unknown | Promise<unknown>>;
|
|
26
25
|
export type ComputeHostOrFns = ComputeHost | ComputeFnMap;
|
|
27
26
|
export declare function createLocalComputeHost(fns: ComputeFnMap): ComputeHost;
|
|
28
27
|
/** Accept either a host or a name→fn map (map is wrapped as local host). */
|
|
@@ -2,24 +2,15 @@ export function parseComputeInvocation(value) {
|
|
|
2
2
|
if (typeof value !== 'object' || value === null || Array.isArray(value))
|
|
3
3
|
return null;
|
|
4
4
|
const record = value;
|
|
5
|
-
if (record.
|
|
6
|
-
(typeof record.
|
|
5
|
+
if (record.args !== undefined &&
|
|
6
|
+
(typeof record.args !== 'object' || record.args === null || Array.isArray(record.args))) {
|
|
7
7
|
return null;
|
|
8
8
|
}
|
|
9
|
-
return {
|
|
10
|
-
...(Object.hasOwn(record, 'caseContext') ? { caseContext: record.caseContext } : {}),
|
|
11
|
-
...(record.params !== undefined ? { params: record.params } : {}),
|
|
12
|
-
};
|
|
9
|
+
return record.args !== undefined ? { args: record.args } : {};
|
|
13
10
|
}
|
|
14
11
|
function isComputeHost(value) {
|
|
15
12
|
return typeof value.execute === 'function';
|
|
16
13
|
}
|
|
17
|
-
function requireCaseContext(name, invocation) {
|
|
18
|
-
if (!Object.hasOwn(invocation, 'caseContext')) {
|
|
19
|
-
throw new Error(`Function ${name}: caseContext required`);
|
|
20
|
-
}
|
|
21
|
-
return invocation.caseContext;
|
|
22
|
-
}
|
|
23
14
|
export function createLocalComputeHost(fns) {
|
|
24
15
|
return {
|
|
25
16
|
async execute(name, invocation = {}) {
|
|
@@ -27,7 +18,7 @@ export function createLocalComputeHost(fns) {
|
|
|
27
18
|
if (!fn) {
|
|
28
19
|
throw new Error(`Missing function implementation: ${name}`);
|
|
29
20
|
}
|
|
30
|
-
return fn(
|
|
21
|
+
return fn((invocation.args ?? {}));
|
|
31
22
|
},
|
|
32
23
|
dispose() { },
|
|
33
24
|
};
|
|
@@ -4,11 +4,11 @@ import type { ComputeHost } from './compute-host.js';
|
|
|
4
4
|
import type { ActionDataSlice, ApiHost, CaseOrchestratorApiHost } from '../fn-context/types.js';
|
|
5
5
|
export type SourceComputeHostOptions = {
|
|
6
6
|
functions: FuncMap;
|
|
7
|
-
/** Company `events` export; handlers are registered under the event name
|
|
7
|
+
/** Company `events` export; handlers are registered under the event name. */
|
|
8
8
|
events?: AnyEventHandlers;
|
|
9
9
|
/** Override ApiHost; default Fake, or Rpc when runToken + data.config.backendUrl. */
|
|
10
10
|
apiHost?: ApiHost & CaseOrchestratorApiHost;
|
|
11
|
-
/** ActionDataSlice used to build
|
|
11
|
+
/** ActionDataSlice used to build every function's ctx. */
|
|
12
12
|
data?: ActionDataSlice;
|
|
13
13
|
/** When set with data.config.backendUrl, uses Http RpcApiHost. */
|
|
14
14
|
runToken?: string;
|
|
@@ -23,15 +23,13 @@ export type SourceComputeHostOptions = {
|
|
|
23
23
|
};
|
|
24
24
|
export interface SourceComputeHost extends ComputeHost {
|
|
25
25
|
}
|
|
26
|
-
/** Company `events` handlers
|
|
26
|
+
/** Company `events` handlers, by event name. */
|
|
27
27
|
export declare function eventsToFuncMap(events: AnyEventHandlers | undefined): FuncMap;
|
|
28
28
|
/**
|
|
29
29
|
* Local host: call author FuncMap implementations directly (no SequenceBuild.code).
|
|
30
30
|
*
|
|
31
|
-
* Lookup by name, then
|
|
32
|
-
*
|
|
33
|
-
* - orchestrator: same, with the Case Orchestrator ctx.
|
|
34
|
-
* - noCase: `fn({ ...ctx, params })`; caseContext must be absent.
|
|
31
|
+
* Lookup by name, then `fn(ctx, args)` (38 §2.1): the Orchestrator gets its own ctx (with `api.tools`),
|
|
32
|
+
* every other function a function's.
|
|
35
33
|
*/
|
|
36
34
|
export declare function createSourceComputeHost(options: SourceComputeHostOptions): SourceComputeHost;
|
|
37
35
|
//# sourceMappingURL=source-compute-host.d.ts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { isSystemEventName
|
|
1
|
+
import { isSystemEventName } from '../core/functions/events.js';
|
|
2
|
+
import { ORCHESTRATOR_FUNCTION } from '../core/case-orchestrator/types.js';
|
|
2
3
|
import { buildActionContext, buildCaseOrchestratorContext } from '../fn-context/build-context.js';
|
|
3
4
|
import { resolveFnParams } from '../fn-context/fn-params.js';
|
|
4
5
|
import { createFakeApiHost } from '../fn-context/fake/index.js';
|
|
@@ -6,28 +7,20 @@ import { createRpcApiHost } from '../fn-context/rpc/index.js';
|
|
|
6
7
|
import { makeCaseOrchestratorCtx, makeCtx } from '../fn-context/make-ctx.js';
|
|
7
8
|
import { getLogger } from '../logger.js';
|
|
8
9
|
const logger = getLogger(['runtime', 'source']);
|
|
9
|
-
|
|
10
|
-
if (!Object.hasOwn(invocation, 'caseContext'))
|
|
11
|
-
throw new Error(`Function ${name}: caseContext required`);
|
|
12
|
-
return invocation.caseContext;
|
|
13
|
-
}
|
|
14
|
-
/** Company `events` handlers as DefinedFunc entries (kind from the platform event table). */
|
|
10
|
+
/** Company `events` handlers, by event name. */
|
|
15
11
|
export function eventsToFuncMap(events) {
|
|
16
12
|
const out = {};
|
|
17
13
|
for (const [name, handler] of Object.entries(events ?? {})) {
|
|
18
|
-
if (
|
|
19
|
-
|
|
20
|
-
out[name] = { kind: systemEventKind(name), main: handler };
|
|
14
|
+
if (isSystemEventName(name) && typeof handler === 'function')
|
|
15
|
+
out[name] = handler;
|
|
21
16
|
}
|
|
22
17
|
return out;
|
|
23
18
|
}
|
|
24
19
|
/**
|
|
25
20
|
* Local host: call author FuncMap implementations directly (no SequenceBuild.code).
|
|
26
21
|
*
|
|
27
|
-
* Lookup by name, then
|
|
28
|
-
*
|
|
29
|
-
* - orchestrator: same, with the Case Orchestrator ctx.
|
|
30
|
-
* - noCase: `fn({ ...ctx, params })`; caseContext must be absent.
|
|
22
|
+
* Lookup by name, then `fn(ctx, args)` (38 §2.1): the Orchestrator gets its own ctx (with `api.tools`),
|
|
23
|
+
* every other function a function's.
|
|
31
24
|
*/
|
|
32
25
|
export function createSourceComputeHost(options) {
|
|
33
26
|
const { functions, data } = options;
|
|
@@ -47,30 +40,16 @@ export function createSourceComputeHost(options) {
|
|
|
47
40
|
}
|
|
48
41
|
return {
|
|
49
42
|
async execute(name, invocation = {}) {
|
|
50
|
-
const
|
|
51
|
-
if (!
|
|
43
|
+
const fn = registry[name];
|
|
44
|
+
if (!fn) {
|
|
52
45
|
throw new Error(`Missing function: ${name}`);
|
|
53
46
|
}
|
|
54
|
-
const
|
|
55
|
-
const params = resolveFnParams(functionInvocation.params);
|
|
56
|
-
if (def.kind === 'case') {
|
|
57
|
-
const caseContext = requireCaseContext(name, functionInvocation);
|
|
58
|
-
const ctx = await systemContext();
|
|
59
|
-
return def.main(caseContext, { ...ctx, params });
|
|
60
|
-
}
|
|
61
|
-
if (def.kind === 'orchestrator') {
|
|
62
|
-
const caseContext = requireCaseContext(name, functionInvocation);
|
|
63
|
-
const ctx = await caseOrchestratorContext();
|
|
64
|
-
return def.main(caseContext, { ...ctx, params: params });
|
|
65
|
-
}
|
|
66
|
-
if (Object.hasOwn(functionInvocation, 'caseContext')) {
|
|
67
|
-
throw new Error(`Function ${name}: noCase function must not receive caseContext`);
|
|
68
|
-
}
|
|
47
|
+
const args = resolveFnParams(invocation.args);
|
|
69
48
|
const contextStarted = Date.now();
|
|
70
|
-
const ctx = await systemContext();
|
|
49
|
+
const ctx = name === ORCHESTRATOR_FUNCTION ? await caseOrchestratorContext() : await systemContext();
|
|
71
50
|
logger.info('context built', { name, durationMs: Date.now() - contextStarted });
|
|
72
51
|
const functionStarted = Date.now();
|
|
73
|
-
const result = await
|
|
52
|
+
const result = await fn(ctx, args);
|
|
74
53
|
logger.info('function executed', { name, durationMs: Date.now() - functionStarted });
|
|
75
54
|
return result;
|
|
76
55
|
},
|
|
@@ -24,7 +24,18 @@ export type ContactHours = {
|
|
|
24
24
|
date: string;
|
|
25
25
|
hours: ContactHoursRange[];
|
|
26
26
|
}[];
|
|
27
|
+
/**
|
|
28
|
+
* Whose hours: left out, the company's contact-hours rules; `contact_schedule`, a contact's own
|
|
29
|
+
* (`contactScheduleHours`); `cadence`, the code's own pacing (one call a day: today closed once it had
|
|
30
|
+
* its call), which holds whatever an environment being tested lets go (`ctx.time.ignore`).
|
|
31
|
+
*/
|
|
32
|
+
source?: 'contact_schedule' | 'cadence';
|
|
27
33
|
};
|
|
34
|
+
/** What does not hold while the environment is tested (`ctx.time.ignore`, 68 §6.2). */
|
|
35
|
+
export type ContactHoursIgnore = Readonly<{
|
|
36
|
+
contactHours: boolean;
|
|
37
|
+
contactSchedule: boolean;
|
|
38
|
+
}>;
|
|
28
39
|
/** The contact's own `contact_schedule` value (when they said they may be reached). */
|
|
29
40
|
export type ContactScheduleValue = {
|
|
30
41
|
weekly: Record<ContactHoursWeekday, ContactHoursRange[]> | null;
|
|
@@ -39,12 +50,21 @@ export declare function everyDay(hours: ContactHoursRange[]): Record<ContactHour
|
|
|
39
50
|
export declare function contactScheduleHours(schedule: ContactScheduleValue | null | undefined): ContactHours;
|
|
40
51
|
/** The hours on a local date (`YYYY-MM-DD`): an override, else a holiday, else the weekday. */
|
|
41
52
|
export declare function contactHoursOn(hours: ContactHours, day: string): ContactHoursRange[];
|
|
42
|
-
/** Whether `at` (a Date, ISO string or epoch ms) falls inside `hours` (one, or a list that must all allow it) in `timezone`. */
|
|
43
|
-
export declare function withinContactHours(hours: ContactHours | readonly ContactHours[], at: Date | string | number, timezone: string): boolean;
|
|
44
53
|
/**
|
|
45
|
-
*
|
|
46
|
-
*
|
|
47
|
-
*
|
|
54
|
+
* Whether `at` (a Date, ISO string or epoch ms) falls inside `hours` (one, or a list that must all allow it)
|
|
55
|
+
* in `timezone`. `ignore` (`ctx.time.ignore`): what does not hold while the environment is tested; when
|
|
56
|
+
* nothing is left to hold, any time is.
|
|
48
57
|
*/
|
|
49
|
-
export declare function
|
|
58
|
+
export declare function withinContactHours(hours: ContactHours | readonly ContactHours[], at: Date | string | number, timezone: string, ignore?: ContactHoursIgnore): boolean;
|
|
59
|
+
/**
|
|
60
|
+
* The first time at or after `from` inside `hours` (one, or a list that must all allow it: the company's
|
|
61
|
+
* rules and the contact's own schedule), in `timezone`, as an ISO string for `at`; null when there is none
|
|
62
|
+
* within `days` (default 14). `from` is `ctx.time` (its `now`, and what does not hold while the environment
|
|
63
|
+
* is tested: `ctx.time.ignore`, 68 §6.2), or a Date, ISO string or epoch ms. When nothing is left to hold,
|
|
64
|
+
* `from` itself.
|
|
65
|
+
*/
|
|
66
|
+
export declare function nextContactTime(hours: ContactHours | readonly ContactHours[], from: Date | string | number | {
|
|
67
|
+
now: number;
|
|
68
|
+
ignore?: ContactHoursIgnore;
|
|
69
|
+
}, timezone: string, days?: number): string | null;
|
|
50
70
|
//# sourceMappingURL=contact-hours.d.ts.map
|
|
@@ -17,8 +17,15 @@ export function contactScheduleHours(schedule) {
|
|
|
17
17
|
return {
|
|
18
18
|
week: schedule?.weekly ?? everyDay(ALL_DAY),
|
|
19
19
|
overrides: (schedule?.dateOverrides ?? []).flatMap((override) => override.dates.map((date) => ({ date, hours: override.windows }))),
|
|
20
|
+
source: 'contact_schedule',
|
|
20
21
|
};
|
|
21
22
|
}
|
|
23
|
+
/** `hours` without what `ignore` lets go: the company's rules, the contact's own schedule; never a cadence. */
|
|
24
|
+
function held(hours, ignore) {
|
|
25
|
+
if (!ignore)
|
|
26
|
+
return hours;
|
|
27
|
+
return hours.filter((entry) => entry.source === 'cadence' ? true : entry.source === 'contact_schedule' ? !ignore.contactSchedule : !ignore.contactHours);
|
|
28
|
+
}
|
|
22
29
|
/** The hours on a local date (`YYYY-MM-DD`): an override, else a holiday, else the weekday. */
|
|
23
30
|
export function contactHoursOn(hours, day) {
|
|
24
31
|
const override = hours.overrides?.find((entry) => entry.date === day);
|
|
@@ -78,21 +85,33 @@ function addDays(day, count) {
|
|
|
78
85
|
function list(hours) {
|
|
79
86
|
return Array.isArray(hours) ? hours : [hours];
|
|
80
87
|
}
|
|
81
|
-
/**
|
|
82
|
-
|
|
88
|
+
/**
|
|
89
|
+
* Whether `at` (a Date, ISO string or epoch ms) falls inside `hours` (one, or a list that must all allow it)
|
|
90
|
+
* in `timezone`. `ignore` (`ctx.time.ignore`): what does not hold while the environment is tested; when
|
|
91
|
+
* nothing is left to hold, any time is.
|
|
92
|
+
*/
|
|
93
|
+
export function withinContactHours(hours, at, timezone, ignore) {
|
|
94
|
+
const kept = held(list(hours), ignore);
|
|
95
|
+
if (kept.length === 0)
|
|
96
|
+
return true;
|
|
83
97
|
const local = localParts(new Date(at), timezone);
|
|
84
|
-
return rangesOn(
|
|
98
|
+
return rangesOn(kept, local.day).some((range) => local.minute >= minutes(range.start) && local.minute < minutes(range.end));
|
|
85
99
|
}
|
|
86
100
|
/**
|
|
87
|
-
* The first time at or after `from`
|
|
88
|
-
*
|
|
89
|
-
*
|
|
101
|
+
* The first time at or after `from` inside `hours` (one, or a list that must all allow it: the company's
|
|
102
|
+
* rules and the contact's own schedule), in `timezone`, as an ISO string for `at`; null when there is none
|
|
103
|
+
* within `days` (default 14). `from` is `ctx.time` (its `now`, and what does not hold while the environment
|
|
104
|
+
* is tested: `ctx.time.ignore`, 68 §6.2), or a Date, ISO string or epoch ms. When nothing is left to hold,
|
|
105
|
+
* `from` itself.
|
|
90
106
|
*/
|
|
91
107
|
export function nextContactTime(hours, from, timezone, days = 14) {
|
|
92
|
-
const
|
|
108
|
+
const time = typeof from === 'object' && !(from instanceof Date) ? from : null;
|
|
109
|
+
const start = new Date(time ? time.now : from).getTime();
|
|
93
110
|
if (!Number.isFinite(start))
|
|
94
111
|
throw new Error('nextContactTime: from must be a time');
|
|
95
|
-
const all = list(hours);
|
|
112
|
+
const all = held(list(hours), time?.ignore);
|
|
113
|
+
if (all.length === 0)
|
|
114
|
+
return new Date(start).toISOString();
|
|
96
115
|
const today = localParts(new Date(start), timezone).day;
|
|
97
116
|
for (let offset = 0; offset <= days; offset += 1) {
|
|
98
117
|
const day = addDays(today, offset);
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
export { ACTIVITY_RECENT_LIMIT, ORCHESTRATOR_ENTRY,
|
|
1
|
+
export { ACTIVITY_RECENT_LIMIT, ORCHESTRATOR_ENTRY, ORCHESTRATOR_FUNCTION, CASE_ORCHESTRATOR_TOOL_NAMES, EMPTY_CASE_ACTIVITY, RECENT_AGREED_ACTIONS_DAYS, type ActivityStats, type AgreementFields, type CaseActivity, type CaseOrchestrator, type CaseOrchestratorApi, type CaseOrchestratorArgs, type CaseOrchestratorContext, type CaseOrchestratorManifest, type CaseOrchestratorParams, type CaseOrchestratorToolName, type CaseOrchestratorTools, type ChannelAvailability, type ChannelUnavailableReason, type ExchangeEntry, type ExchangeEvent, type ExchangeSearchHit, type ContactActionInput, type ContactActionView, type ContactChannel, type PartyContactHours, type FinishedAgreedActionView, type PartyActivity, type ReadExchangeResult, type RescheduledResult, type RunTrigger, type RunTriggerType, type ScheduleUpdateSlotsInput, type SlotFieldWriteInput, type SlotInsertInput, type SlotWriteInput, type ScheduleTiming, type ScheduledActionInput, type ScheduledActionInputs, type ScheduledActionKind, type ScheduledActionTiming, type ScheduledActionView, type SearchExchangesResult, type ToolCallArgs, type ToolCallResult, type ToolResult, type UpdateSlotsActionInput, type UpdateSlotsActionView, type ScheduleWakeInput, type WakeActionInput, type WakeActionView, type WakeResult, } from './types.js';
|
|
2
2
|
export { localDateTime, localDay, render, zonedIso, type ConversationOptions, type TimelineOptions } from './render.js';
|
|
3
|
-
export { tools } from './tools.js';
|
|
4
|
-
export { followUps } from './follow-ups.js';
|
|
5
|
-
export { validateCaseOrchestratorResult, type CaseOrchestratorResultCheck } from './validate.js';
|
|
6
3
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
export { ACTIVITY_RECENT_LIMIT, ORCHESTRATOR_ENTRY,
|
|
1
|
+
export { ACTIVITY_RECENT_LIMIT, ORCHESTRATOR_ENTRY, ORCHESTRATOR_FUNCTION, CASE_ORCHESTRATOR_TOOL_NAMES, EMPTY_CASE_ACTIVITY, RECENT_AGREED_ACTIONS_DAYS, } from './types.js';
|
|
2
2
|
export { localDateTime, localDay, render, zonedIso } from './render.js';
|
|
3
|
-
export { tools } from './tools.js';
|
|
4
|
-
export { followUps } from './follow-ups.js';
|
|
5
|
-
export { validateCaseOrchestratorResult } from './validate.js';
|
|
6
3
|
//# sourceMappingURL=index.js.map
|
|
@@ -55,7 +55,7 @@ export type ConversationOptions = {
|
|
|
55
55
|
*/
|
|
56
56
|
declare function conversation(result: ReadExchangeResult, timezone: string, options?: ConversationOptions): string;
|
|
57
57
|
/**
|
|
58
|
-
* Text for company
|
|
58
|
+
* Text for company code to show or log (docs/design/08-case-orchestrator.md §5.3), in the mars next-step
|
|
59
59
|
* format. Party functions take one contact's activity (`activity.byContact[contactId]`) and
|
|
60
60
|
* write its local wall-clock times.
|
|
61
61
|
*/
|
|
@@ -158,20 +158,22 @@ function pendingActions(actions, timezone) {
|
|
|
158
158
|
return actions
|
|
159
159
|
.map((action) => {
|
|
160
160
|
const head = `- ${action.id}: ${action.kind} at ${localDateTime(action.scheduledFor, timezone)}`;
|
|
161
|
+
const why = action.reason ? ` (${action.reason})` : '';
|
|
161
162
|
if (action.kind === 'update_slots') {
|
|
162
|
-
const { timing, writes
|
|
163
|
-
return `${head}
|
|
163
|
+
const { timing, writes } = action.input;
|
|
164
|
+
return `${head}${why} ${JSON.stringify({ timing, writes })}`;
|
|
164
165
|
}
|
|
165
166
|
if (action.kind === 'next_sequence') {
|
|
166
|
-
const { timing, sequence, subjectRecordId
|
|
167
|
-
return `${head}
|
|
167
|
+
const { timing, sequence, subjectRecordId } = action.input;
|
|
168
|
+
return `${head}${why} ${JSON.stringify({ timing, sequence, ...(subjectRecordId ? { subjectRecordId } : {}) })}`;
|
|
168
169
|
}
|
|
169
170
|
if (action.kind === 'wake') {
|
|
170
|
-
const { timing
|
|
171
|
-
|
|
171
|
+
const { timing } = action.input;
|
|
172
|
+
const name = action.name;
|
|
173
|
+
return `${head} ${name}${why} ${JSON.stringify({ timing })}`;
|
|
172
174
|
}
|
|
173
|
-
const { timing
|
|
174
|
-
return `${head}${agreedMark(action, timezone)}${
|
|
175
|
+
const { timing } = action.input;
|
|
176
|
+
return `${head}${agreedMark(action, timezone)}${why} ${JSON.stringify({ timing })}`;
|
|
175
177
|
})
|
|
176
178
|
.join('\n');
|
|
177
179
|
}
|
|
@@ -190,6 +192,9 @@ function wakeUps(triggers, timezone) {
|
|
|
190
192
|
return [`- external ${trigger.reason} at ${at}`];
|
|
191
193
|
if (trigger.type === 'inbound.paused')
|
|
192
194
|
return [`- inbound ${trigger.channel} ${trigger.exchangeId} at ${at}, not answered: the case was paused`];
|
|
195
|
+
if (trigger.type === 'scheduled_action.ended') {
|
|
196
|
+
return [`- scheduled ${trigger.kind} ${trigger.scheduledActionId} at ${at}: ${trigger.outcome === 'not_sent' ? `not sent (${trigger.reason ?? 'decided'})` : 'slots written'}`];
|
|
197
|
+
}
|
|
193
198
|
return [];
|
|
194
199
|
});
|
|
195
200
|
return lines.length > 0 ? lines.join('\n') : '(none)';
|
|
@@ -202,7 +207,7 @@ function recentAgreedActions(actions, timezone) {
|
|
|
202
207
|
if (actions.length === 0)
|
|
203
208
|
return '(none)';
|
|
204
209
|
return actions
|
|
205
|
-
.map((action) => `- ${action.id}: ${action.kind} at ${localDateTime(action.scheduledFor, timezone)}${agreedMark(action, timezone)}, ${action.status} at ${localDateTime(action.finishedAt, timezone)}${action.
|
|
210
|
+
.map((action) => `- ${action.id}: ${action.kind} at ${localDateTime(action.scheduledFor, timezone)}${agreedMark(action, timezone)}, ${action.status} at ${localDateTime(action.finishedAt, timezone)}${action.reason ? ` (${action.reason})` : ''}`)
|
|
206
211
|
.join('\n');
|
|
207
212
|
}
|
|
208
213
|
/**
|
|
@@ -220,7 +225,7 @@ function conversation(result, timezone, options = {}) {
|
|
|
220
225
|
return `${head}\n${earlier}${lines.join('\n')}`;
|
|
221
226
|
}
|
|
222
227
|
/**
|
|
223
|
-
* Text for company
|
|
228
|
+
* Text for company code to show or log (docs/design/08-case-orchestrator.md §5.3), in the mars next-step
|
|
224
229
|
* format. Party functions take one contact's activity (`activity.byContact[contactId]`) and
|
|
225
230
|
* write its local wall-clock times.
|
|
226
231
|
*/
|