@ixo/oracle-runtime-workers 0.13.0 → 0.16.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 +32 -10
- package/package.json +13 -8
- package/src/artifacts/config.ts +158 -0
- package/src/artifacts/crypto.ts +94 -0
- package/src/artifacts/routes.ts +73 -0
- package/src/artifacts/store.ts +238 -0
- package/src/artifacts/sweep.ts +200 -0
- package/src/artifacts/tool.ts +103 -0
- package/src/artifacts/viewer-page.ts +614 -0
- package/src/attachments/download.ts +168 -45
- package/src/attachments/index.ts +9 -0
- package/src/attachments/magic.ts +33 -0
- package/src/attachments/pipeline.ts +105 -49
- package/src/attachments/sandbox-archive.ts +9 -8
- package/src/attachments/view-cache.ts +142 -0
- package/src/attachments/view.ts +53 -12
- package/src/channels/auth.ts +310 -0
- package/src/channels/contract.ts +246 -0
- package/src/channels/turns.ts +280 -0
- package/src/core/budgeted-llm.ts +223 -0
- package/src/core/capability-router.ts +273 -0
- package/src/core/context-budget.ts +97 -10
- package/src/core/env.ts +111 -0
- package/src/core/execution-profile.ts +17 -0
- package/src/core/index.ts +267 -9
- package/src/core/llm.ts +30 -9
- package/src/core/main-agent-types.ts +41 -1
- package/src/core/main-agent.ts +367 -65
- package/src/core/manifest.ts +34 -0
- package/src/core/meta-tools.ts +128 -18
- package/src/core/middlewares/capability-gate.ts +130 -12
- package/src/core/middlewares/context-guard.ts +55 -24
- package/src/core/middlewares/index.ts +20 -2
- package/src/core/middlewares/page-context.ts +46 -2
- package/src/core/middlewares/return-direct-first.ts +47 -0
- package/src/core/middlewares/summarization.ts +152 -10
- package/src/core/middlewares/tool-execution.ts +323 -0
- package/src/core/middlewares/tool-repetition-guard.ts +202 -35
- package/src/core/middlewares/turn-boundary.ts +162 -0
- package/src/core/openrouter-pricing.ts +155 -22
- package/src/core/prompt-composer.ts +112 -7
- package/src/core/registries.ts +253 -48
- package/src/core/request-admission.ts +159 -0
- package/src/core/runtime-context.ts +107 -8
- package/src/core/state.ts +1 -1
- package/src/core/sub-agent-fallback.ts +21 -2
- package/src/core/subagent-as-tool.ts +78 -43
- package/src/core/tool-access.ts +189 -0
- package/src/core/tool-scheduler.ts +125 -0
- package/src/core/turn-budget.ts +205 -0
- package/src/core/turn-time-note.ts +43 -0
- package/src/core/user-kv.ts +446 -0
- package/src/core/wrap-plugin-tool.ts +20 -0
- package/src/delivery/plan.ts +278 -0
- package/src/delivery/profile.ts +161 -0
- package/src/delivery/prompt.ts +27 -0
- package/src/delivery/schema.ts +61 -0
- package/src/delivery/shaper.ts +389 -0
- package/src/delivery/types.ts +84 -0
- package/src/do/ambient.ts +51 -15
- package/src/do/contracts.ts +170 -6
- package/src/do/idle-eviction.ts +38 -0
- package/src/do/matrix-turn-ledger.ts +24 -5
- package/src/do/result-store-test-do.ts +137 -3
- package/src/do/result-store.ts +239 -58
- package/src/do/room-mirror.ts +29 -0
- package/src/do/run-buffer.ts +64 -9
- package/src/do/run-coordinator.ts +451 -76
- package/src/do/run-request.ts +2 -0
- package/src/do/run-store-test-do.ts +301 -1
- package/src/do/run-store.ts +385 -43
- package/src/do/secrets-adapter.ts +22 -3
- package/src/do/sse-stream.ts +60 -9
- package/src/do/transcript.ts +48 -5
- package/src/do/turn-body.ts +105 -5
- package/src/do/ucan-service.ts +204 -34
- package/src/do/user-oracle-do.ts +2003 -274
- package/src/feedback/config.ts +127 -0
- package/src/feedback/contract.ts +148 -0
- package/src/feedback/linear-sink.ts +285 -0
- package/src/feedback/privacy.ts +150 -0
- package/src/feedback/reservation.ts +298 -0
- package/src/feedback/submit.ts +247 -0
- package/src/index.ts +138 -6
- package/src/llm/byo-adapter.ts +27 -6
- package/src/llm/byo-catalog.ts +13 -0
- package/src/llm/byo-model-fallback.ts +248 -0
- package/src/llm/byo-service.ts +45 -8
- package/src/llm/provider-error.ts +139 -2
- package/src/matrix/gateway-do.ts +504 -108
- package/src/matrix/group-chat.ts +66 -23
- package/src/matrix/inbox-store.ts +16 -1
- package/src/matrix/ingest.ts +79 -14
- package/src/matrix/media-deadline.ts +130 -0
- package/src/matrix/replay-format.ts +5 -3
- package/src/matrix/reply-parts.ts +92 -0
- package/src/matrix/room-alias.ts +96 -0
- package/src/matrix/user-homeserver.ts +166 -7
- package/src/owner-store/ixo-vfs-store.ts +170 -43
- package/src/owner-store/migrating-store.ts +31 -15
- package/src/owner-store/types.ts +18 -3
- package/src/plugin-api/index.ts +43 -2
- package/src/plugin-api/oracle-plugin.ts +35 -0
- package/src/plugin-api/request-admission.ts +41 -0
- package/src/plugin-api/tool-helper.ts +25 -1
- package/src/plugin-api/tool-plane.ts +92 -0
- package/src/plugin-api/types.ts +194 -6
- package/src/plugins/agui/agui.plugin.ts +31 -16
- package/src/plugins/composio/composio-tools.ts +57 -6
- package/src/plugins/composio/composio-ucan.ts +3 -2
- package/src/plugins/composio/composio.plugin.ts +2 -0
- package/src/plugins/delegated-capabilities.ts +25 -0
- package/src/plugins/editor/content-session.ts +173 -83
- package/src/plugins/editor/content-tools.ts +76 -17
- package/src/plugins/editor/editor-mx.ts +28 -9
- package/src/plugins/editor/editor.plugin.ts +3 -1
- package/src/plugins/editor/failures.ts +28 -7
- package/src/plugins/editor/prompts.ts +2 -1
- package/src/plugins/editor/provider.ts +553 -40
- package/src/plugins/editor/standalone-editor-tool.ts +61 -18
- package/src/plugins/firecrawl/firecrawl-tools.ts +13 -5
- package/src/plugins/flows/action-metadata.ts +111 -11
- package/src/plugins/flows/actions.ts +18 -0
- package/src/plugins/flows/decision-context.ts +31 -0
- package/src/plugins/flows/edit.ts +223 -52
- package/src/plugins/flows/errors.ts +22 -1
- package/src/plugins/flows/flow-doc.ts +92 -9
- package/src/plugins/flows/flows.plugin.ts +11 -5
- package/src/plugins/flows/forms.ts +34 -0
- package/src/plugins/flows/index.ts +2 -0
- package/src/plugins/flows/input-policy.ts +140 -0
- package/src/plugins/flows/prompts.ts +14 -11
- package/src/plugins/flows/read.ts +49 -9
- package/src/plugins/flows/ref-syntax.ts +129 -0
- package/src/plugins/flows/test-homeserver.ts +245 -0
- package/src/plugins/flows/tools/authoring.ts +216 -112
- package/src/plugins/flows/tools/discovery.ts +4 -1
- package/src/plugins/flows/tools/forms.ts +21 -2
- package/src/plugins/flows/tools/settings.ts +34 -13
- package/src/plugins/flows/translator.ts +13 -10
- package/src/plugins/flows/types.ts +167 -42
- package/src/plugins/index.ts +11 -1
- package/src/plugins/ixo-transaction/chain-ids.ts +43 -0
- package/src/plugins/ixo-transaction/index.ts +2 -0
- package/src/plugins/ixo-transaction/ixo-transaction.plugin.ts +119 -0
- package/src/plugins/ixo-transaction/receipts.ts +99 -0
- package/src/plugins/ixo-transaction/tools.ts +329 -0
- package/src/plugins/ixo-transaction/wallet-signing.ts +202 -0
- package/src/plugins/mcp-tool-adapter.ts +23 -2
- package/src/plugins/memory/memory-tools.ts +7 -2
- package/src/plugins/memory/memory-ucan.ts +3 -2
- package/src/plugins/memory/memory.plugin.ts +2 -0
- package/src/plugins/pod-creator/blueprint-store.ts +163 -0
- package/src/plugins/pod-creator/blueprint-types.ts +61 -0
- package/src/plugins/pod-creator/bounded-map.ts +87 -0
- package/src/plugins/pod-creator/capsule-content-client.ts +285 -0
- package/src/plugins/pod-creator/chain-gateway.ts +110 -0
- package/src/plugins/pod-creator/config.ts +85 -0
- package/src/plugins/pod-creator/create-session-store.ts +180 -0
- package/src/plugins/pod-creator/create-tools.ts +538 -0
- package/src/plugins/pod-creator/design-pod-roles.ts +111 -0
- package/src/plugins/pod-creator/index.ts +65 -0
- package/src/plugins/pod-creator/orchestration-tools.ts +172 -0
- package/src/plugins/pod-creator/pod-creator.plugin.ts +190 -0
- package/src/plugins/pod-creator/stage.ts +201 -0
- package/src/plugins/pod-creator/sub-agents.ts +189 -0
- package/src/plugins/portal/action-log.ts +21 -1
- package/src/plugins/portal/declared-tools.ts +90 -0
- package/src/plugins/portal/portal.plugin.ts +42 -16
- package/src/plugins/sandbox/sandbox-bridge.ts +79 -10
- package/src/plugins/sandbox/sandbox-mcp.ts +3 -2
- package/src/plugins/sandbox/sandbox-write-blob.ts +11 -1
- package/src/plugins/sandbox/sandbox.plugin.ts +8 -16
- package/src/plugins/tasks/manifest.ts +1 -1
- package/src/plugins/tasks/middleware.ts +20 -7
- package/src/plugins/tasks/tasks-tools.ts +30 -3
- package/src/plugins/tasks/tasks.plugin.ts +64 -11
- package/src/plugins/user-preferences/schema.ts +12 -1
- package/src/plugins/user-preferences/user-preferences-store.ts +38 -13
- package/src/plugins/user-preferences/user-preferences-tool.ts +3 -2
- package/src/plugins/vfs/vfs-client.ts +250 -71
- package/src/plugins/vfs/vfs-content.ts +30 -11
- package/src/plugins/vfs/vfs-errors.ts +22 -0
- package/src/plugins/vfs/vfs-sandbox-tools.ts +12 -5
- package/src/plugins/vfs/vfs-tools.ts +60 -20
- package/src/realtime/frontend-call-registry.ts +228 -81
- package/src/realtime/realtime-endpoint.ts +215 -44
- package/src/realtime/session-socket-hub.ts +47 -2
- package/src/secrets/secrets-service.ts +9 -3
- package/src/shell/app.ts +562 -82
- package/src/shell/auth.ts +131 -37
- package/src/shell/turn-body-cap.ts +11 -13
- package/src/sqlite/blob-compactor.ts +61 -28
- package/src/sqlite/database.ts +156 -90
- package/src/sqlite/do-vfs.ts +416 -129
- package/src/sqlite/page-tier.ts +133 -40
- package/src/sqlite/serialization.ts +40 -0
- package/src/sqlite/sessions-store.ts +10 -2
- package/src/sqlite/sqlite-saver.ts +139 -19
- package/src/sqlite/user-kv-store.ts +317 -0
- package/src/tasks/schedule.ts +201 -12
- package/src/tasks/scheduler.ts +586 -98
- package/src/tasks/spec.ts +67 -3
- package/src/tasks/store.ts +292 -27
- package/src/tasks/topic-deliverables.ts +70 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
export {
|
|
2
|
+
PodCreatorPlugin,
|
|
3
|
+
type PodCreatorPluginOptions,
|
|
4
|
+
} from './pod-creator.plugin';
|
|
5
|
+
export {
|
|
6
|
+
CapsuleContentClient,
|
|
7
|
+
DEFAULT_CAPSULES_BASE_URL,
|
|
8
|
+
createRegistryInstructionsFetcher,
|
|
9
|
+
MAX_CAPSULE_INSTRUCTIONS_BYTES,
|
|
10
|
+
type CapsuleContentClientOptions,
|
|
11
|
+
type CapsuleContentFetcher,
|
|
12
|
+
type CapsuleFetchContext,
|
|
13
|
+
type CapsuleUcanBuilder,
|
|
14
|
+
} from './capsule-content-client';
|
|
15
|
+
export {
|
|
16
|
+
DESIGN_POD_ROLES,
|
|
17
|
+
DESIGN_POD_STAGES,
|
|
18
|
+
type DesignPodRole,
|
|
19
|
+
type DesignPodStage,
|
|
20
|
+
} from './design-pod-roles';
|
|
21
|
+
export {
|
|
22
|
+
type BlueprintSection,
|
|
23
|
+
type PodBlueprint,
|
|
24
|
+
type ServicePodBlueprint,
|
|
25
|
+
} from './blueprint-types';
|
|
26
|
+
export {
|
|
27
|
+
KvBlueprintStore,
|
|
28
|
+
type BlueprintStore,
|
|
29
|
+
type BlueprintStoreFor,
|
|
30
|
+
} from './blueprint-store';
|
|
31
|
+
export {
|
|
32
|
+
SPECIALISTS_FOR_STAGE,
|
|
33
|
+
STAGE_ORDER,
|
|
34
|
+
assembleServicePodBlueprint,
|
|
35
|
+
computeReadiness,
|
|
36
|
+
deriveStage,
|
|
37
|
+
type Readiness,
|
|
38
|
+
} from './stage';
|
|
39
|
+
export { createOrchestrationTools } from './orchestration-tools';
|
|
40
|
+
export { buildStageSubAgents } from './sub-agents';
|
|
41
|
+
export {
|
|
42
|
+
SIGN_TRANSACTION_ACTION,
|
|
43
|
+
createCreateTools,
|
|
44
|
+
type RequestPodSignatureResult,
|
|
45
|
+
} from './create-tools';
|
|
46
|
+
export {
|
|
47
|
+
POD_BATCH_TYPE_URLS,
|
|
48
|
+
notConfiguredChainGateway,
|
|
49
|
+
podBatchProblem,
|
|
50
|
+
type ChainGateway,
|
|
51
|
+
type CreatedPod,
|
|
52
|
+
type PreparedPodBatch,
|
|
53
|
+
} from './chain-gateway';
|
|
54
|
+
export {
|
|
55
|
+
KvCreateSessionStore,
|
|
56
|
+
type ApproveOutcome,
|
|
57
|
+
type CreateSessionStore,
|
|
58
|
+
type CreateSessionStoreFor,
|
|
59
|
+
} from './create-session-store';
|
|
60
|
+
export {
|
|
61
|
+
DEFAULT_NETWORK,
|
|
62
|
+
podCreatorConfigSchema,
|
|
63
|
+
readPodCreatorConfig,
|
|
64
|
+
} from './config';
|
|
65
|
+
export { BoundedMap, type BoundedMapOptions } from './bounded-map';
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { computeSubAgentToolName } from '../../core/subagent-as-tool';
|
|
3
|
+
import { tool } from '../../plugin-api/tool-helper';
|
|
4
|
+
import type { PluginTool, RuntimeContext } from '../../plugin-api/types';
|
|
5
|
+
import type { BlueprintStoreFor } from './blueprint-store';
|
|
6
|
+
import type { CreateSessionStoreFor } from './create-session-store';
|
|
7
|
+
import {
|
|
8
|
+
SPECIALISTS_FOR_STAGE,
|
|
9
|
+
assembleServicePodBlueprint,
|
|
10
|
+
computeReadiness,
|
|
11
|
+
sectionsView,
|
|
12
|
+
} from './stage';
|
|
13
|
+
|
|
14
|
+
const startSchema = z.object({
|
|
15
|
+
brief: z
|
|
16
|
+
.string()
|
|
17
|
+
.min(1)
|
|
18
|
+
.describe(
|
|
19
|
+
'A concise statement of what the POD should do — the service, who it serves, and the outcome it produces.',
|
|
20
|
+
),
|
|
21
|
+
restart: z
|
|
22
|
+
.boolean()
|
|
23
|
+
.optional()
|
|
24
|
+
.describe(
|
|
25
|
+
'Discard any existing design in this conversation and start fresh with the new brief.',
|
|
26
|
+
),
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const getBlueprintSchema = z.object({
|
|
30
|
+
roles: z
|
|
31
|
+
.array(z.string())
|
|
32
|
+
.optional()
|
|
33
|
+
.describe(
|
|
34
|
+
'Role ids whose full section content to include. Omit for the compact per-section summary.',
|
|
35
|
+
),
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
const emptySchema = z.object({});
|
|
39
|
+
|
|
40
|
+
const threadId = (ctx: RuntimeContext): string => ctx.session.id;
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The conductor's orchestration tools. They own the blueprint lifecycle —
|
|
44
|
+
* starting a session, scoring readiness, and assembling the final
|
|
45
|
+
* `service_pod_blueprint`. Sections are written ONLY by the specialist
|
|
46
|
+
* sub-agents' `submit_section`; the conductor gets no write path, so it cannot
|
|
47
|
+
* self-certify gate verdicts and skip the specialists. Stage and readiness are
|
|
48
|
+
* always derived from the recorded sections, never stored.
|
|
49
|
+
*
|
|
50
|
+
* The stores are resolved per call from the request context: on Workers they
|
|
51
|
+
* are the user's own database (`ctx.kv`), not plugin-instance memory.
|
|
52
|
+
* Discarding a design also discards any batch prepared or approved for it, so
|
|
53
|
+
* nothing prepared from the old design can reach the wallet.
|
|
54
|
+
*/
|
|
55
|
+
export function createOrchestrationTools(
|
|
56
|
+
storeFor: BlueprintStoreFor,
|
|
57
|
+
sessionsFor: CreateSessionStoreFor,
|
|
58
|
+
): PluginTool[] {
|
|
59
|
+
const startPodDesign = tool(
|
|
60
|
+
async (args, ctx) => {
|
|
61
|
+
const { brief, restart } = startSchema.parse(args);
|
|
62
|
+
const store = storeFor(ctx);
|
|
63
|
+
if (restart) {
|
|
64
|
+
await sessionsFor(ctx).clear(ctx.user.did, threadId(ctx));
|
|
65
|
+
await store.reset(threadId(ctx));
|
|
66
|
+
}
|
|
67
|
+
const bp = await store.init(threadId(ctx), brief);
|
|
68
|
+
const readiness = computeReadiness(bp);
|
|
69
|
+
return {
|
|
70
|
+
started: true,
|
|
71
|
+
threadId: bp.threadId,
|
|
72
|
+
brief: bp.brief,
|
|
73
|
+
stage: readiness.stage,
|
|
74
|
+
nextSpecialists: SPECIALISTS_FOR_STAGE[readiness.stage].map((id) =>
|
|
75
|
+
computeSubAgentToolName(id),
|
|
76
|
+
),
|
|
77
|
+
};
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: 'start_pod_design',
|
|
81
|
+
description:
|
|
82
|
+
'Open a POD design session and initialise the blueprint. Call this first when the user wants to create a POD; pass restart=true to discard the existing design and begin again.',
|
|
83
|
+
schema: startSchema,
|
|
84
|
+
},
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
const getBlueprint = tool(
|
|
88
|
+
async (args, ctx) => {
|
|
89
|
+
const { roles } = getBlueprintSchema.parse(args);
|
|
90
|
+
const bp = await storeFor(ctx).get(threadId(ctx));
|
|
91
|
+
if (!bp) {
|
|
92
|
+
return {
|
|
93
|
+
started: false,
|
|
94
|
+
message:
|
|
95
|
+
'No POD design session started yet. Call start_pod_design first.',
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
started: true,
|
|
100
|
+
...(bp.brief !== undefined ? { brief: bp.brief } : {}),
|
|
101
|
+
readiness: computeReadiness(bp),
|
|
102
|
+
...sectionsView(bp, roles),
|
|
103
|
+
};
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
name: 'get_blueprint',
|
|
107
|
+
description:
|
|
108
|
+
'Read the POD blueprint: readiness plus a compact per-section summary. Pass roles=[...] to include the full content of specific sections.',
|
|
109
|
+
schema: getBlueprintSchema,
|
|
110
|
+
effect: 'read',
|
|
111
|
+
},
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
const computeReadinessTool = tool(
|
|
115
|
+
async (_args, ctx) => {
|
|
116
|
+
const bp = await storeFor(ctx).get(threadId(ctx));
|
|
117
|
+
if (!bp) {
|
|
118
|
+
return {
|
|
119
|
+
started: false,
|
|
120
|
+
message: 'No POD design session started yet.',
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
return computeReadiness(bp);
|
|
124
|
+
},
|
|
125
|
+
{
|
|
126
|
+
name: 'compute_readiness',
|
|
127
|
+
description:
|
|
128
|
+
"Score the blueprint's launch readiness and list the blockers that remain.",
|
|
129
|
+
schema: emptySchema,
|
|
130
|
+
effect: 'read',
|
|
131
|
+
},
|
|
132
|
+
);
|
|
133
|
+
|
|
134
|
+
const assembleBlueprint = tool(
|
|
135
|
+
async (_args, ctx) => {
|
|
136
|
+
const bp = await storeFor(ctx).get(threadId(ctx));
|
|
137
|
+
if (!bp) {
|
|
138
|
+
throw new Error(
|
|
139
|
+
'No POD design session started yet. Call start_pod_design first.',
|
|
140
|
+
);
|
|
141
|
+
}
|
|
142
|
+
const readiness = computeReadiness(bp);
|
|
143
|
+
if (!readiness.complete) {
|
|
144
|
+
return {
|
|
145
|
+
assembled: false,
|
|
146
|
+
stage: readiness.stage,
|
|
147
|
+
blockers: readiness.blockers,
|
|
148
|
+
message:
|
|
149
|
+
'Launch-readiness gate not passed. Resolve the blockers before assembling.',
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
assembled: true,
|
|
154
|
+
blueprint: assembleServicePodBlueprint(bp),
|
|
155
|
+
};
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
name: 'assemble_blueprint',
|
|
159
|
+
description:
|
|
160
|
+
'Assemble the final service_pod_blueprint once the launch-readiness gate passes.',
|
|
161
|
+
schema: emptySchema,
|
|
162
|
+
effect: 'read',
|
|
163
|
+
},
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
return [
|
|
167
|
+
startPodDesign,
|
|
168
|
+
getBlueprint,
|
|
169
|
+
computeReadinessTool,
|
|
170
|
+
assembleBlueprint,
|
|
171
|
+
];
|
|
172
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { OraclePlugin } from '../../plugin-api/oracle-plugin';
|
|
3
|
+
import type {
|
|
4
|
+
PluginManifest,
|
|
5
|
+
PluginSubAgent,
|
|
6
|
+
PluginTool,
|
|
7
|
+
RuntimeContext,
|
|
8
|
+
UserKvSurface,
|
|
9
|
+
} from '../../plugin-api/types';
|
|
10
|
+
import { KvBlueprintStore, type BlueprintStore } from './blueprint-store';
|
|
11
|
+
import {
|
|
12
|
+
CapsuleContentClient,
|
|
13
|
+
type CapsuleContentClientOptions,
|
|
14
|
+
type CapsuleContentFetcher,
|
|
15
|
+
} from './capsule-content-client';
|
|
16
|
+
import { notConfiguredChainGateway, type ChainGateway } from './chain-gateway';
|
|
17
|
+
import { podCreatorConfigSchema } from './config';
|
|
18
|
+
import {
|
|
19
|
+
KvCreateSessionStore,
|
|
20
|
+
type CreateSessionStore,
|
|
21
|
+
} from './create-session-store';
|
|
22
|
+
import { createCreateTools } from './create-tools';
|
|
23
|
+
import { createOrchestrationTools } from './orchestration-tools';
|
|
24
|
+
import { buildStageSubAgents } from './sub-agents';
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Sibling env read at request time to configure the capsule client. The
|
|
28
|
+
* registry URL is owned by the skills plugin's configSchema and `NETWORK` by
|
|
29
|
+
* the base env schema; pod-creator reads them without redeclaring (which would
|
|
30
|
+
* collide in the config-schema registry).
|
|
31
|
+
*/
|
|
32
|
+
const capsuleEnvSchema = z.object({
|
|
33
|
+
SKILLS_CAPSULES_BASE_URL: z.url().optional(),
|
|
34
|
+
NETWORK: z.enum(['mainnet', 'testnet', 'devnet']).optional(),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
const manifest: PluginManifest = {
|
|
38
|
+
title: 'POD Creator',
|
|
39
|
+
summary:
|
|
40
|
+
'Design and create an IXO Programmable Organisational Domain (POD) end to end — qualify, architect, build, evaluate, then prepare an on-chain creation batch for the user to sign.',
|
|
41
|
+
whenToUse: [
|
|
42
|
+
'User wants to create, design, or stand up a new POD (Programmable Organisational Domain) or service domain on IXO.',
|
|
43
|
+
'User describes a service or organisation they want to launch and asks the oracle to turn it into a working POD.',
|
|
44
|
+
],
|
|
45
|
+
whenNotToUse: [
|
|
46
|
+
'Operating or supporting an already-live POD — out of scope for now.',
|
|
47
|
+
'Generic on-chain entity creation unrelated to POD design.',
|
|
48
|
+
],
|
|
49
|
+
examples: [
|
|
50
|
+
{
|
|
51
|
+
user: 'Help me create a POD for a community solar monitoring service.',
|
|
52
|
+
thought:
|
|
53
|
+
'A POD creation request — open the design session, then drive the specialists stage by stage.',
|
|
54
|
+
tool: 'start_pod_design',
|
|
55
|
+
args: { brief: 'Community solar monitoring service' },
|
|
56
|
+
},
|
|
57
|
+
],
|
|
58
|
+
tags: ['pod', 'domain', 'design', 'orchestration', 'on-chain'],
|
|
59
|
+
category: 'automation',
|
|
60
|
+
visibility: 'on-demand',
|
|
61
|
+
stability: 'experimental',
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
export interface PodCreatorPluginOptions {
|
|
65
|
+
/**
|
|
66
|
+
* Registry retrieval for capsule `SKILL.md` text. When omitted, specialist
|
|
67
|
+
* sub-agents run on their built-in prompts. Pass
|
|
68
|
+
* `createRegistryInstructionsFetcher()` to load the registry's published
|
|
69
|
+
* instructions (`GET /skills/{name}/instructions`), or a test stub.
|
|
70
|
+
*/
|
|
71
|
+
capsuleContentFetcher?: CapsuleContentFetcher;
|
|
72
|
+
/**
|
|
73
|
+
* Chain gateway for the create path. Injected by tests; the real IXO MCP
|
|
74
|
+
* server binding wires in here. When omitted, the create tools report
|
|
75
|
+
* on-chain creation as unavailable.
|
|
76
|
+
*/
|
|
77
|
+
chainGateway?: ChainGateway;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* The plugin's durable state lives in the user's own database; a host
|
|
82
|
+
* without `ctx.kv` cannot run the design pipeline without silently losing
|
|
83
|
+
* the blueprint, so it is refused loudly instead.
|
|
84
|
+
*/
|
|
85
|
+
function requireUserKv(ctx: RuntimeContext): UserKvSurface {
|
|
86
|
+
if (!ctx.kv) {
|
|
87
|
+
throw new Error(
|
|
88
|
+
'pod-creator needs the host per-user store (ctx.kv), which this host does not provide.',
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
return ctx.kv;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* POD-creator plugin.
|
|
96
|
+
*
|
|
97
|
+
* Runs the design-pod lifecycle: a conductor (the main agent) drives the
|
|
98
|
+
* specialist sub-agents to design a POD, then prepares an unsigned on-chain
|
|
99
|
+
* creation batch for the user's wallet to sign. Each specialist's instructions
|
|
100
|
+
* are loaded from the ai-skills capsule registry per stage via the
|
|
101
|
+
* `CapsuleContentClient`.
|
|
102
|
+
*
|
|
103
|
+
* Exposes the conductor's orchestration tools (the blueprint lifecycle), the
|
|
104
|
+
* stage-gated specialist sub-agents, and the on-chain create path — a
|
|
105
|
+
* propose → approve → commit handoff with single-use approvals, ending in the
|
|
106
|
+
* blocking `sign_transaction` AG-UI round-trip. The chain encoding sits behind
|
|
107
|
+
* an injectable gateway.
|
|
108
|
+
*
|
|
109
|
+
* Per-thread blueprints and create sessions are rows in the user's own
|
|
110
|
+
* database (`ctx.kv`), so a design survives the user object being evicted and
|
|
111
|
+
* restored from its owner copy. Only the capsule-text cache is held on the
|
|
112
|
+
* instance.
|
|
113
|
+
*/
|
|
114
|
+
export class PodCreatorPlugin extends OraclePlugin {
|
|
115
|
+
readonly name = 'pod-creator';
|
|
116
|
+
readonly version = '0.1.0';
|
|
117
|
+
readonly manifest = manifest;
|
|
118
|
+
override readonly softDependsOn = [
|
|
119
|
+
'agui',
|
|
120
|
+
'editor',
|
|
121
|
+
'domain-indexer',
|
|
122
|
+
'memory',
|
|
123
|
+
];
|
|
124
|
+
override readonly configSchema = podCreatorConfigSchema;
|
|
125
|
+
|
|
126
|
+
private readonly capsuleContentFetcher?: CapsuleContentFetcher;
|
|
127
|
+
|
|
128
|
+
private readonly chainGateway: ChainGateway;
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Built lazily from request config; cached so the per-thread prompt cache
|
|
132
|
+
* survives across requests.
|
|
133
|
+
*/
|
|
134
|
+
private capsuleContent?: CapsuleContentClient;
|
|
135
|
+
|
|
136
|
+
constructor(options: PodCreatorPluginOptions = {}) {
|
|
137
|
+
super();
|
|
138
|
+
this.capsuleContentFetcher = options.capsuleContentFetcher;
|
|
139
|
+
this.chainGateway = options.chainGateway ?? notConfiguredChainGateway;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
override getTools(): PluginTool[] {
|
|
143
|
+
return [
|
|
144
|
+
...createOrchestrationTools(blueprintStoreFor, createSessionStoreFor),
|
|
145
|
+
...createCreateTools(
|
|
146
|
+
blueprintStoreFor,
|
|
147
|
+
this.chainGateway,
|
|
148
|
+
createSessionStoreFor,
|
|
149
|
+
),
|
|
150
|
+
];
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
override async getRequestSubAgents(
|
|
154
|
+
rt: RuntimeContext,
|
|
155
|
+
): Promise<PluginSubAgent[]> {
|
|
156
|
+
// This hook runs on every turn whether or not the capability is loaded:
|
|
157
|
+
// on a host without the store there is no design to offer specialists
|
|
158
|
+
// for, and the tools themselves report the missing store when called.
|
|
159
|
+
if (!rt.kv) return [];
|
|
160
|
+
return buildStageSubAgents(rt, blueprintStoreFor, this.capsuleClient(rt));
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
private capsuleClient(rt: RuntimeContext): CapsuleContentClient {
|
|
164
|
+
if (!this.capsuleContent) {
|
|
165
|
+
const env = capsuleEnvSchema.safeParse(rt.config);
|
|
166
|
+
const options: CapsuleContentClientOptions = {};
|
|
167
|
+
if (env.success) {
|
|
168
|
+
if (env.data.SKILLS_CAPSULES_BASE_URL !== undefined) {
|
|
169
|
+
options.baseUrl = env.data.SKILLS_CAPSULES_BASE_URL;
|
|
170
|
+
}
|
|
171
|
+
if (env.data.NETWORK !== undefined) {
|
|
172
|
+
options.network = env.data.NETWORK;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (this.capsuleContentFetcher !== undefined) {
|
|
176
|
+
options.fetcher = this.capsuleContentFetcher;
|
|
177
|
+
}
|
|
178
|
+
this.capsuleContent = new CapsuleContentClient(options);
|
|
179
|
+
}
|
|
180
|
+
return this.capsuleContent;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
function blueprintStoreFor(ctx: RuntimeContext): BlueprintStore {
|
|
185
|
+
return new KvBlueprintStore(requireUserKv(ctx));
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
function createSessionStoreFor(ctx: RuntimeContext): CreateSessionStore {
|
|
189
|
+
return new KvCreateSessionStore(requireUserKv(ctx));
|
|
190
|
+
}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
BlueprintSection,
|
|
3
|
+
PodBlueprint,
|
|
4
|
+
ServicePodBlueprint,
|
|
5
|
+
} from './blueprint-types';
|
|
6
|
+
import {
|
|
7
|
+
DESIGN_POD_ROLES,
|
|
8
|
+
DESIGN_POD_STAGES,
|
|
9
|
+
type DesignPodStage,
|
|
10
|
+
} from './design-pod-roles';
|
|
11
|
+
|
|
12
|
+
/** The lifecycle stages in readiness order. */
|
|
13
|
+
export const STAGE_ORDER: readonly DesignPodStage[] = DESIGN_POD_STAGES;
|
|
14
|
+
|
|
15
|
+
/** Role ids required to complete each stage, grouped from the role catalogue. */
|
|
16
|
+
function groupRolesByStage(): Record<DesignPodStage, string[]> {
|
|
17
|
+
const out: Record<DesignPodStage, string[]> = {
|
|
18
|
+
qualify: [],
|
|
19
|
+
architect: [],
|
|
20
|
+
build: [],
|
|
21
|
+
evaluate: [],
|
|
22
|
+
package: [],
|
|
23
|
+
gate: [],
|
|
24
|
+
};
|
|
25
|
+
for (const role of DESIGN_POD_ROLES) {
|
|
26
|
+
out[role.stage].push(role.id);
|
|
27
|
+
}
|
|
28
|
+
return out;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export const SPECIALISTS_FOR_STAGE: Record<DesignPodStage, readonly string[]> =
|
|
32
|
+
groupRolesByStage();
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* A section counts toward readiness when it exists — and, for gate-bearing
|
|
36
|
+
* roles (the evaluate oracles and the launch-readiness gate), only when it
|
|
37
|
+
* carries an explicit `'pass'` verdict. This stops a gate role that submitted
|
|
38
|
+
* content without a verdict from silently satisfying the launch gate.
|
|
39
|
+
*/
|
|
40
|
+
function sectionSatisfies(section: BlueprintSection | undefined): boolean {
|
|
41
|
+
if (section === undefined) {
|
|
42
|
+
return false;
|
|
43
|
+
}
|
|
44
|
+
const isGateRole = section.stage === 'evaluate' || section.stage === 'gate';
|
|
45
|
+
return isGateRole ? section.verdict === 'pass' : section.verdict !== 'fail';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* The current stage: the first stage in {@link STAGE_ORDER} whose required
|
|
50
|
+
* sections are not all satisfied. A failed gate verdict leaves its stage
|
|
51
|
+
* unsatisfied, so the stage re-opens until the specialist is re-run. When every
|
|
52
|
+
* stage is satisfied, returns `'gate'` (and {@link computeReadiness} reports
|
|
53
|
+
* `complete: true`).
|
|
54
|
+
*/
|
|
55
|
+
export function deriveStage(bp: PodBlueprint): DesignPodStage {
|
|
56
|
+
for (const stage of STAGE_ORDER) {
|
|
57
|
+
const allSatisfied = SPECIALISTS_FOR_STAGE[stage].every((roleId) =>
|
|
58
|
+
sectionSatisfies(bp.sections[roleId]),
|
|
59
|
+
);
|
|
60
|
+
if (!allSatisfied) {
|
|
61
|
+
return stage;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return 'gate';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/** Readiness summary derived from the blueprint's recorded sections. */
|
|
68
|
+
export interface Readiness {
|
|
69
|
+
/** The stage the conductor should drive next. */
|
|
70
|
+
stage: DesignPodStage;
|
|
71
|
+
/** True once every required section is satisfied (gate passed). */
|
|
72
|
+
complete: boolean;
|
|
73
|
+
/** Stages whose required sections are all satisfied. */
|
|
74
|
+
completedStages: DesignPodStage[];
|
|
75
|
+
/** Human-readable blockers (missing sections, failed gates). */
|
|
76
|
+
blockers: string[];
|
|
77
|
+
/** Fraction of required sections satisfied, `0..1`. */
|
|
78
|
+
score: number;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function computeReadiness(bp: PodBlueprint): Readiness {
|
|
82
|
+
const blockers: string[] = [];
|
|
83
|
+
const completedStages: DesignPodStage[] = [];
|
|
84
|
+
let satisfied = 0;
|
|
85
|
+
let required = 0;
|
|
86
|
+
|
|
87
|
+
for (const stage of STAGE_ORDER) {
|
|
88
|
+
let stageComplete = true;
|
|
89
|
+
for (const roleId of SPECIALISTS_FOR_STAGE[stage]) {
|
|
90
|
+
required += 1;
|
|
91
|
+
const section = bp.sections[roleId];
|
|
92
|
+
if (sectionSatisfies(section)) {
|
|
93
|
+
satisfied += 1;
|
|
94
|
+
} else {
|
|
95
|
+
stageComplete = false;
|
|
96
|
+
if (section === undefined) {
|
|
97
|
+
blockers.push(`${stage}: ${roleId} not recorded`);
|
|
98
|
+
} else if (section.verdict === 'fail') {
|
|
99
|
+
const detail = (section.blockers ?? []).join('; ') || 'no detail';
|
|
100
|
+
blockers.push(`${stage}: ${roleId} failed (${detail})`);
|
|
101
|
+
} else {
|
|
102
|
+
blockers.push(`${stage}: ${roleId} awaiting a pass verdict`);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
if (stageComplete) {
|
|
107
|
+
completedStages.push(stage);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return {
|
|
112
|
+
stage: deriveStage(bp),
|
|
113
|
+
complete: required > 0 && satisfied === required,
|
|
114
|
+
completedStages,
|
|
115
|
+
blockers,
|
|
116
|
+
score: required === 0 ? 0 : satisfied / required,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Compact per-section view for blueprint reads. Sections carry arbitrary-size
|
|
122
|
+
* role-defined payloads, so blueprint reads return this summary by default and
|
|
123
|
+
* full content only for explicitly requested roles.
|
|
124
|
+
*/
|
|
125
|
+
export function summarizeSection(section: BlueprintSection): {
|
|
126
|
+
role: string;
|
|
127
|
+
stage: DesignPodStage;
|
|
128
|
+
verdict?: 'pass' | 'fail';
|
|
129
|
+
recordedAt: string;
|
|
130
|
+
contentBytes: number;
|
|
131
|
+
} {
|
|
132
|
+
return {
|
|
133
|
+
role: section.role,
|
|
134
|
+
stage: section.stage,
|
|
135
|
+
...(section.verdict !== undefined ? { verdict: section.verdict } : {}),
|
|
136
|
+
recordedAt: section.recordedAt,
|
|
137
|
+
contentBytes: JSON.stringify(section.content ?? null).length,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* The blueprint read shared by the conductor's `get_blueprint` and the
|
|
143
|
+
* specialists' `read_blueprint`: the compact per-section summary, plus the
|
|
144
|
+
* full content of the requested roles when `roles` is given.
|
|
145
|
+
*/
|
|
146
|
+
export function sectionsView(
|
|
147
|
+
bp: PodBlueprint,
|
|
148
|
+
roles: readonly string[] | undefined,
|
|
149
|
+
): {
|
|
150
|
+
sections: ReturnType<typeof summarizeSection>[];
|
|
151
|
+
content?: Record<string, unknown>;
|
|
152
|
+
} {
|
|
153
|
+
const sections = Object.values(bp.sections);
|
|
154
|
+
return {
|
|
155
|
+
sections: sections.map(summarizeSection),
|
|
156
|
+
...(roles !== undefined
|
|
157
|
+
? {
|
|
158
|
+
content: Object.fromEntries(
|
|
159
|
+
sections
|
|
160
|
+
.filter((section) => roles.includes(section.role))
|
|
161
|
+
.map((section) => [section.role, section.content]),
|
|
162
|
+
),
|
|
163
|
+
}
|
|
164
|
+
: {}),
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/** Group the recorded sections by stage, preserving the stage ordering. */
|
|
169
|
+
function groupSectionsByStage(
|
|
170
|
+
bp: PodBlueprint,
|
|
171
|
+
): Record<DesignPodStage, BlueprintSection[]> {
|
|
172
|
+
const out: Record<DesignPodStage, BlueprintSection[]> = {
|
|
173
|
+
qualify: [],
|
|
174
|
+
architect: [],
|
|
175
|
+
build: [],
|
|
176
|
+
evaluate: [],
|
|
177
|
+
package: [],
|
|
178
|
+
gate: [],
|
|
179
|
+
};
|
|
180
|
+
// The stored blueprint is schema-checked on read, so every section's stage
|
|
181
|
+
// is one of the catalogue's.
|
|
182
|
+
for (const section of Object.values(bp.sections)) {
|
|
183
|
+
out[section.stage].push(section);
|
|
184
|
+
}
|
|
185
|
+
return out;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/** Assemble the final `service_pod_blueprint` from a completed blueprint. */
|
|
189
|
+
export function assembleServicePodBlueprint(
|
|
190
|
+
bp: PodBlueprint,
|
|
191
|
+
): ServicePodBlueprint {
|
|
192
|
+
const blueprint: ServicePodBlueprint = {
|
|
193
|
+
threadId: bp.threadId,
|
|
194
|
+
stages: groupSectionsByStage(bp),
|
|
195
|
+
assembledAt: new Date().toISOString(),
|
|
196
|
+
};
|
|
197
|
+
if (bp.brief !== undefined) {
|
|
198
|
+
blueprint.brief = bp.brief;
|
|
199
|
+
}
|
|
200
|
+
return blueprint;
|
|
201
|
+
}
|