@ixo/oracle-runtime-workers 0.23.0 → 0.25.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/package.json +5 -4
- package/src/action-proposals/store.ts +0 -40
- package/src/action-proposals/topic-store.ts +0 -42
- package/src/core/env.ts +4 -1
- package/src/delivery/plan.ts +4 -1
- package/src/do/contracts.ts +0 -1
- package/src/do/user-oracle-do.ts +278 -71
- package/src/hub-actions/client.ts +58 -5
- package/src/hub-actions/outcome-signature.ts +227 -0
- package/src/hub-actions/request.ts +16 -7
- package/src/hub-actions/store.ts +61 -37
- package/src/hub-actions/transfer.ts +182 -0
- package/src/memory/session-history-indexer.ts +30 -3
- package/src/memory/user-context-fetcher.ts +36 -12
- package/src/plugin-api/types.ts +25 -2
- package/src/plugins/hub-actions/hub-actions.plugin.ts +154 -25
- package/src/plugins/memory/index.ts +4 -0
- package/src/plugins/memory/memory-entity.ts +49 -0
- package/src/plugins/memory/memory-tools.ts +7 -4
- package/src/plugins/memory/memory-ucan.ts +10 -1
- package/src/plugins/memory/memory.plugin.ts +22 -7
- package/src/shell/app.ts +36 -20
|
@@ -14,12 +14,16 @@ import {
|
|
|
14
14
|
fetchMemoryTools,
|
|
15
15
|
type MemoryMcpFactory,
|
|
16
16
|
} from './memory-tools';
|
|
17
|
+
import { oracleEntityDidSchema } from './memory-entity';
|
|
17
18
|
|
|
18
19
|
const configSchema = z.object({
|
|
19
20
|
MEMORY_MCP_URL: z.string().url('MEMORY_MCP_URL must be a valid HTTP(S) URL.'),
|
|
20
21
|
MEMORY_ENGINE_URL: z
|
|
21
22
|
.string()
|
|
22
23
|
.url('MEMORY_ENGINE_URL must be a valid HTTP(S) URL.'),
|
|
24
|
+
// The Memory Engine keys every space by the oracle's domain entity; an
|
|
25
|
+
// oracle without one gets no memory at all, so it fails at boot instead.
|
|
26
|
+
ORACLE_ENTITY_DID: oracleEntityDidSchema,
|
|
23
27
|
});
|
|
24
28
|
|
|
25
29
|
const manifest: PluginManifest = {
|
|
@@ -51,9 +55,11 @@ const manifest: PluginManifest = {
|
|
|
51
55
|
export interface MemoryPluginOptions {
|
|
52
56
|
/**
|
|
53
57
|
* Override the MCP-tools factory. Defaults to an HTTP MCP client against
|
|
54
|
-
* `MEMORY_MCP_URL` authenticated with a per-request UCAN invocation
|
|
58
|
+
* `MEMORY_MCP_URL` authenticated with a per-request UCAN invocation and
|
|
59
|
+
* the oracle's domain entity (`ORACLE_ENTITY_DID`, the engine's
|
|
60
|
+
* `x-oracle-entity-did` header).
|
|
55
61
|
*/
|
|
56
|
-
mcpFactory?: (memoryMcpUrl: string) => MemoryMcpFactory;
|
|
62
|
+
mcpFactory?: (memoryMcpUrl: string, entityDid: string) => MemoryMcpFactory;
|
|
57
63
|
/**
|
|
58
64
|
* Filter the upstream tool surface. Defaults to {@link DEFAULT_MEMORY_TOOLS}
|
|
59
65
|
* (search + add + add-oracle-knowledge + delete-episode + clear). Pass an
|
|
@@ -62,9 +68,15 @@ export interface MemoryPluginOptions {
|
|
|
62
68
|
selectedTools?: readonly string[];
|
|
63
69
|
}
|
|
64
70
|
|
|
65
|
-
function
|
|
71
|
+
function resolveMemoryConfig(config: MergedConfig): {
|
|
72
|
+
memoryMcpUrl: string;
|
|
73
|
+
entityDid: string;
|
|
74
|
+
} {
|
|
66
75
|
const parsed = configSchema.parse(config);
|
|
67
|
-
return
|
|
76
|
+
return {
|
|
77
|
+
memoryMcpUrl: parsed.MEMORY_MCP_URL,
|
|
78
|
+
entityDid: parsed.ORACLE_ENTITY_DID,
|
|
79
|
+
};
|
|
68
80
|
}
|
|
69
81
|
|
|
70
82
|
/**
|
|
@@ -90,7 +102,10 @@ export class MemoryPlugin extends OraclePlugin {
|
|
|
90
102
|
|
|
91
103
|
override readonly autoDetectHint = 'MEMORY_MCP_URL';
|
|
92
104
|
|
|
93
|
-
private readonly mcpFactory: (
|
|
105
|
+
private readonly mcpFactory: (
|
|
106
|
+
memoryMcpUrl: string,
|
|
107
|
+
entityDid: string,
|
|
108
|
+
) => MemoryMcpFactory;
|
|
94
109
|
|
|
95
110
|
private readonly selectedTools: readonly string[];
|
|
96
111
|
|
|
@@ -107,8 +122,8 @@ export class MemoryPlugin extends OraclePlugin {
|
|
|
107
122
|
}
|
|
108
123
|
|
|
109
124
|
override async getRequestTools(rtCtx: RuntimeContext): Promise<PluginTool[]> {
|
|
110
|
-
const
|
|
111
|
-
const factory = this.mcpFactory(
|
|
125
|
+
const { memoryMcpUrl, entityDid } = resolveMemoryConfig(rtCtx.config);
|
|
126
|
+
const factory = this.mcpFactory(memoryMcpUrl, entityDid);
|
|
112
127
|
return fetchMemoryTools(rtCtx, factory, this.selectedTools);
|
|
113
128
|
}
|
|
114
129
|
|
package/src/shell/app.ts
CHANGED
|
@@ -78,22 +78,7 @@ import {
|
|
|
78
78
|
} from '../feedback/submit';
|
|
79
79
|
import { z } from 'zod';
|
|
80
80
|
import { parseOutcome } from '@ixo/action-protocol';
|
|
81
|
-
|
|
82
|
-
async function equalOutcomeKeys(
|
|
83
|
-
provided: string | null,
|
|
84
|
-
expected: string | undefined,
|
|
85
|
-
): Promise<boolean> {
|
|
86
|
-
if (!provided || !expected || expected.length < 32 || provided.length > 512)
|
|
87
|
-
return false;
|
|
88
|
-
const digest = async (value: string) =>
|
|
89
|
-
new Uint8Array(
|
|
90
|
-
await crypto.subtle.digest('SHA-256', new TextEncoder().encode(value)),
|
|
91
|
-
);
|
|
92
|
-
const [a, b] = await Promise.all([digest(provided), digest(expected)]);
|
|
93
|
-
let difference = 0;
|
|
94
|
-
for (let i = 0; i < a.length; i++) difference |= (a[i] ?? 0) ^ (b[i] ?? 0);
|
|
95
|
-
return difference === 0;
|
|
96
|
-
}
|
|
81
|
+
import { verifyHubOutcomeCallback } from '../hub-actions/outcome-signature';
|
|
97
82
|
|
|
98
83
|
const DebugEventBody = z.object({
|
|
99
84
|
type: z
|
|
@@ -217,6 +202,22 @@ export const BYO_LLM_BODY_BYTES = 64 * 1024;
|
|
|
217
202
|
export const DEBUG_BODY_BYTES = 64 * 1024;
|
|
218
203
|
|
|
219
204
|
/** A body cap enforced before the body is buffered: over it, 413 and the handler never runs. */
|
|
205
|
+
/**
|
|
206
|
+
* The audience a signed Hub outcome callback must be signed for: the origin
|
|
207
|
+
* of `ORACLE_PUBLIC_URL` when it is set, else the origin of the request URL.
|
|
208
|
+
* An unparsable public URL yields '' (which never verifies).
|
|
209
|
+
*/
|
|
210
|
+
export function callbackAudience(
|
|
211
|
+
publicUrl: string | undefined,
|
|
212
|
+
requestUrl: string,
|
|
213
|
+
): string {
|
|
214
|
+
try {
|
|
215
|
+
return new URL(publicUrl || requestUrl).origin;
|
|
216
|
+
} catch {
|
|
217
|
+
return '';
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
|
|
220
221
|
function limitBody(maxSize: number) {
|
|
221
222
|
return bodyLimit({
|
|
222
223
|
maxSize,
|
|
@@ -455,17 +456,32 @@ export function createShell(
|
|
|
455
456
|
});
|
|
456
457
|
|
|
457
458
|
app.use('/api/internal/actions/outcomes', limitBody(64 * 1024));
|
|
459
|
+
// Auth Hub's signed outcome callbacks: on whenever Hub actions are
|
|
460
|
+
// configured. The signature is checked against the Hub's own did:web key
|
|
461
|
+
// (from AUTH_HUB_URL) for this oracle's origin, then the owner object
|
|
462
|
+
// confirms the outcome through Auth Hub's authenticated status API.
|
|
458
463
|
app.post('/api/internal/actions/outcomes', async (c) => {
|
|
459
464
|
if (
|
|
460
465
|
c.env.HUB_ACTIONS_ENABLED !== 'true' ||
|
|
461
|
-
!
|
|
462
|
-
|
|
463
|
-
c.env.AUTH_HUB_ACTION_OUTCOME_KEY,
|
|
464
|
-
))
|
|
466
|
+
!c.env.AUTH_HUB_URL ||
|
|
467
|
+
!c.env.AUTH_HUB_CHANNEL_SERVICE_KEY
|
|
465
468
|
)
|
|
466
469
|
return c.json({ code: 'unauthorized' }, 401);
|
|
467
470
|
const body = await c.req.raw.text();
|
|
468
471
|
if (body.length > 64_000) return c.json({ code: 'request_too_large' }, 413);
|
|
472
|
+
if (
|
|
473
|
+
!(await verifyHubOutcomeCallback({
|
|
474
|
+
hubUrl: c.env.AUTH_HUB_URL,
|
|
475
|
+
rawBody: body,
|
|
476
|
+
headers: c.req.raw.headers,
|
|
477
|
+
// The origin the Hub signs for: the configured public URL when
|
|
478
|
+
// set (a Worker reachable under several hostnames), else the
|
|
479
|
+
// origin this request came in on.
|
|
480
|
+
audience: callbackAudience(c.env.ORACLE_PUBLIC_URL, c.req.url),
|
|
481
|
+
...(c.env.AUTH_HUB ? { fetcher: c.env.AUTH_HUB } : {}),
|
|
482
|
+
}))
|
|
483
|
+
)
|
|
484
|
+
return c.json({ code: 'unauthorized' }, 401);
|
|
469
485
|
try {
|
|
470
486
|
const outcome = parseOutcome(JSON.parse(body));
|
|
471
487
|
await userStub(c.env, outcome.ownerDid).recordHubActionOutcome(outcome);
|