trellis 3.1.35 → 3.2.2
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 +23 -5
- package/README.md +63 -156
- package/bin/trellis.mjs +2 -75
- package/dist/{better-sqlite-backend-ahx5p0br.js → better-sqlite-backend-2P4F6AOJ.js} +91 -29
- package/dist/boot-middleware-MHLHFCUC.js +9 -0
- package/dist/browser/index.d.ts +18 -0
- package/dist/browser/index.d.ts.map +1 -0
- package/dist/browser/index.js +95 -0
- package/dist/{index-53f3b8p8.js → chunk-2W3MHTMG.js} +552 -213
- package/dist/chunk-44FFQKQX.js +142 -0
- package/dist/chunk-4HYPLFJD.js +150 -0
- package/dist/chunk-4XIVMVHC.js +361 -0
- package/dist/{index-yp88he8n.js → chunk-6CTA6MCE.js} +88 -64
- package/dist/chunk-6GEZ6DN5.js +394 -0
- package/dist/chunk-7ZFRVCUE.js +211 -0
- package/dist/chunk-CAG4FULI.js +121 -0
- package/dist/chunk-FF36CQHZ.js +61 -0
- package/dist/{index-h32txmxe.js → chunk-FKQO5A3H.js} +6 -13
- package/dist/{index-nq520y6k.js → chunk-FWRS4CSC.js} +46 -32
- package/dist/chunk-GFZCJ4EH.js +108 -0
- package/dist/chunk-GLM4HGN4.js +55 -0
- package/dist/{index-4cdr7x2x.js → chunk-GU374KWZ.js} +182 -111
- package/dist/chunk-H6JB3PZ3.js +45 -0
- package/dist/chunk-IZX2PLIB.js +1240 -0
- package/dist/chunk-J2TXUFCZ.js +289 -0
- package/dist/chunk-J3WYZO3Q.js +211 -0
- package/dist/{index-xzym9w0m.js → chunk-JA7AIHRK.js} +12 -7
- package/dist/chunk-JHHEXEDM.js +162 -0
- package/dist/chunk-JUEMDWLU.js +233 -0
- package/dist/chunk-KFQGP6VL.js +33 -0
- package/dist/chunk-KIJITUZB.js +419 -0
- package/dist/{index-4wxa8xz4.js → chunk-KQ4A2D2P.js} +162 -23
- package/dist/chunk-KSU2GW22.js +958 -0
- package/dist/{index-rv1p47gp.js → chunk-L5N5PR2S.js} +3015 -2480
- package/dist/{index-h9e2efx4.js → chunk-LNUIGRDZ.js} +71 -55
- package/dist/{index-skhn0agf.js → chunk-MLC5BUYO.js} +19 -20
- package/dist/chunk-MPXUVGT3.js +104 -0
- package/dist/chunk-N6MYZT4O.js +68 -0
- package/dist/chunk-O2AV5WMN.js +48 -0
- package/dist/chunk-PZ4XYZ4J.js +143 -0
- package/dist/chunk-QB5ISE47.js +419 -0
- package/dist/chunk-RSWUFTO2.js +857 -0
- package/dist/chunk-S3XLFZ27.js +1095 -0
- package/dist/{index-w5wccer3.js → chunk-S6GBJ2ZB.js} +28 -12
- package/dist/{index-7pjz3tsy.js → chunk-SGMDTWJJ.js} +57 -15
- package/dist/chunk-SPSPV5BC.js +390 -0
- package/dist/chunk-TCKXSOBP.js +123 -0
- package/dist/chunk-UBGUDMUV.js +59 -0
- package/dist/{index-wt8rz4gn.js → chunk-UQISRW6T.js} +97 -26
- package/dist/chunk-USEINYUL.js +12 -0
- package/dist/{index-bmyt7k8n.js → chunk-UZRUP7QW.js} +12 -4
- package/dist/{index-xhcp6xrn.js → chunk-VBYUTRXX.js} +695 -514
- package/dist/{index-vkpkfwhq.js → chunk-VDAKEOML.js} +234 -194
- package/dist/chunk-WXSWA3MV.js +0 -0
- package/dist/{index-n9f2qyh5.js → chunk-X5FLTRUB.js} +55 -33
- package/dist/{index-h7zxhhhh.js → chunk-XMQ7G77X.js} +46 -38
- package/dist/{index-w7ng765c.js → chunk-YCM7ZYEZ.js} +102 -49
- package/dist/cli/deploy-cli.d.ts +18 -0
- package/dist/cli/deploy-cli.d.ts.map +1 -0
- package/dist/cli/errors.d.ts +6 -0
- package/dist/cli/errors.d.ts.map +1 -0
- package/dist/cli/examples.d.ts +23 -0
- package/dist/cli/examples.d.ts.map +1 -0
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.d.ts.map +1 -1
- package/dist/cli/index.js +2426 -6225
- package/dist/client/index.browser.d.ts +29 -0
- package/dist/client/index.browser.d.ts.map +1 -0
- package/dist/client/index.browser.js +64 -0
- package/dist/client/index.d.ts +6 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +41 -21
- package/dist/client/live.d.ts +67 -0
- package/dist/client/live.d.ts.map +1 -0
- package/dist/client/reactive.d.ts +31 -0
- package/dist/client/reactive.d.ts.map +1 -0
- package/dist/client/sdk.browser.d.ts +104 -0
- package/dist/client/sdk.browser.d.ts.map +1 -0
- package/dist/client/sdk.browser.js +9 -0
- package/dist/client/sdk.d.ts +26 -2
- package/dist/client/sdk.d.ts.map +1 -1
- package/dist/client/sdk.js +9 -0
- package/dist/client/vcs-client.d.ts +129 -0
- package/dist/client/vcs-client.d.ts.map +1 -0
- package/dist/cms/index.js +171 -163
- package/dist/{config-8hczw0rs.js → config-PFTP67TR.js} +8 -9
- package/dist/core/computation/index.d.ts +5 -0
- package/dist/core/computation/index.d.ts.map +1 -0
- package/dist/core/computation/rollup.d.ts +18 -0
- package/dist/core/computation/rollup.d.ts.map +1 -0
- package/dist/core/index.d.ts +5 -0
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +171 -104
- package/dist/core/kernel/boot-middleware.d.ts +10 -0
- package/dist/core/kernel/boot-middleware.d.ts.map +1 -0
- package/dist/core/kernel/logic-middleware.d.ts +7 -1
- package/dist/core/kernel/logic-middleware.d.ts.map +1 -1
- package/dist/core/kernel/trellis-kernel.d.ts.map +1 -1
- package/dist/core/ontology/types.d.ts +5 -0
- package/dist/core/ontology/types.d.ts.map +1 -1
- package/dist/core/persist/better-sqlite-backend.d.ts +9 -2
- package/dist/core/persist/better-sqlite-backend.d.ts.map +1 -1
- package/dist/core/persist/factory.d.ts +6 -7
- package/dist/core/persist/factory.d.ts.map +1 -1
- package/dist/core/persist/factory.js +2 -3
- package/dist/core/persist/sqljs-backend.js +2 -3
- package/dist/db/index.d.ts +4 -1
- package/dist/db/index.d.ts.map +1 -1
- package/dist/db/index.js +67 -54
- package/dist/db/inspector.js +11 -19
- package/dist/db/trellis.css +1 -1
- package/dist/decisions/index.js +9 -10
- package/dist/deploy-LQ7GUGJ5.js +9 -0
- package/dist/deploy-YNVG5E4E.js +9 -0
- package/dist/deploy-cli-24BCHAXY.js +67 -0
- package/dist/deploy-cli-C35HTITO.js +67 -0
- package/dist/embeddings/auto-embed.d.ts +2 -2
- package/dist/embeddings/auto-embed.d.ts.map +1 -1
- package/dist/embeddings/index.js +20 -22
- package/dist/embeddings/search.d.ts +2 -1
- package/dist/embeddings/search.d.ts.map +1 -1
- package/dist/embeddings/store.d.ts +25 -4
- package/dist/embeddings/store.d.ts.map +1 -1
- package/dist/engine.d.ts +59 -19
- package/dist/engine.d.ts.map +1 -1
- package/dist/import-UZEKXU3B.js +10 -0
- package/dist/index.js +102 -93
- package/dist/launch-explorer-HZ4ZJA4X.js +95 -0
- package/dist/links/index.js +24 -26
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/plugins/agent-memory/index.js +316 -0
- package/dist/plugins/idea-garden/index.js +165 -0
- package/dist/plugins/plan-approval/index.js +579 -0
- package/dist/plugins/proactive-watcher/index.js +194 -0
- package/dist/{index-2r4jxwnb.js → query-XYLTJYSA.js} +14 -15
- package/dist/react/index.js +39 -70
- package/dist/react/realtime.js +11 -0
- package/dist/react/schema-hooks.js +87 -0
- package/dist/realtime/broadcast-channel-transport.d.ts +43 -0
- package/dist/realtime/broadcast-channel-transport.d.ts.map +1 -0
- package/dist/realtime/durable-object-relay-transport.d.ts +96 -0
- package/dist/realtime/durable-object-relay-transport.d.ts.map +1 -0
- package/dist/realtime/index.d.ts +44 -0
- package/dist/realtime/index.d.ts.map +1 -0
- package/dist/realtime/index.js +40 -0
- package/dist/realtime/memory-hub.d.ts +32 -0
- package/dist/realtime/memory-hub.d.ts.map +1 -0
- package/dist/realtime/persistent-channel.d.ts +105 -0
- package/dist/realtime/persistent-channel.d.ts.map +1 -0
- package/dist/realtime/presence.d.ts +73 -0
- package/dist/realtime/presence.d.ts.map +1 -0
- package/dist/realtime/relay-persistence.d.ts +37 -0
- package/dist/realtime/relay-persistence.d.ts.map +1 -0
- package/dist/realtime/relay-persistence.js +107 -0
- package/dist/realtime/relay-server.d.ts +103 -0
- package/dist/realtime/relay-server.d.ts.map +1 -0
- package/dist/realtime/room.d.ts +98 -0
- package/dist/realtime/room.d.ts.map +1 -0
- package/dist/realtime/text.d.ts +73 -0
- package/dist/realtime/text.d.ts.map +1 -0
- package/dist/realtime/types.d.ts +89 -0
- package/dist/realtime/types.d.ts.map +1 -0
- package/dist/realtime/websocket-relay-transport.d.ts +28 -0
- package/dist/realtime/websocket-relay-transport.d.ts.map +1 -0
- package/dist/realtime.bundle.js +1100 -0
- package/dist/{remote-manager-xvbg4230.js → remote-manager-ODE3T2KR.js} +25 -15
- package/dist/scaffold/seed.d.ts.map +1 -1
- package/dist/scaffold/write.d.ts +1 -1
- package/dist/scaffold/write.d.ts.map +1 -1
- package/dist/schema/define.d.ts +137 -0
- package/dist/schema/define.d.ts.map +1 -0
- package/dist/schema/entity-projection.d.ts +34 -0
- package/dist/schema/entity-projection.d.ts.map +1 -0
- package/dist/schema/eql.d.ts +46 -0
- package/dist/schema/eql.d.ts.map +1 -0
- package/dist/schema/index.d.ts +21 -0
- package/dist/schema/index.d.ts.map +1 -0
- package/dist/schema/index.js +46 -0
- package/dist/schema/kernel-resolve.d.ts +21 -0
- package/dist/schema/kernel-resolve.d.ts.map +1 -0
- package/dist/schema/mutations.d.ts +20 -0
- package/dist/schema/mutations.d.ts.map +1 -0
- package/dist/schema/resolve.d.ts +21 -0
- package/dist/schema/resolve.d.ts.map +1 -0
- package/dist/server/deploy-meta.d.ts +25 -0
- package/dist/server/deploy-meta.d.ts.map +1 -0
- package/dist/server/deploy.d.ts +7 -2
- package/dist/server/deploy.d.ts.map +1 -1
- package/dist/server/index.d.ts +7 -2
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +100 -58
- package/dist/server/node-adapter.js +5 -104
- package/dist/server/realtime.d.ts +14 -3
- package/dist/server/realtime.d.ts.map +1 -1
- package/dist/server/server.d.ts +8 -16
- package/dist/server/server.d.ts.map +1 -1
- package/dist/server/sprites.d.ts +18 -1
- package/dist/server/sprites.d.ts.map +1 -1
- package/dist/server/tenancy.d.ts +4 -0
- package/dist/server/tenancy.d.ts.map +1 -1
- package/dist/server/usage-meter.d.ts +53 -0
- package/dist/server/usage-meter.d.ts.map +1 -0
- package/dist/server-MB3OPSPI.js +18 -0
- package/dist/server-ZVWGLLRF.js +18 -0
- package/dist/sprites-KQGTGM6W.js +27 -0
- package/dist/{sprites-vc4qbrp1.js → sprites-RU3E4XQN.js} +6 -7
- package/dist/svelte/index.d.ts +17 -0
- package/dist/svelte/index.d.ts.map +1 -0
- package/dist/svelte/index.js +51 -0
- package/dist/svelte/schema-hooks.d.ts +45 -0
- package/dist/svelte/schema-hooks.d.ts.map +1 -0
- package/dist/svelte/schema-hooks.js +71 -0
- package/dist/svelte/stores.d.ts +78 -0
- package/dist/svelte/stores.d.ts.map +1 -0
- package/dist/sync/http-transport.d.ts +3 -3
- package/dist/sync/http-transport.d.ts.map +1 -1
- package/dist/sync/index.d.ts +15 -1
- package/dist/sync/index.d.ts.map +1 -1
- package/dist/sync/index.js +617 -0
- package/dist/sync/memory-room.d.ts +37 -0
- package/dist/sync/memory-room.d.ts.map +1 -0
- package/dist/sync/memory-transport.d.ts +3 -3
- package/dist/sync/memory-transport.d.ts.map +1 -1
- package/dist/sync/partykit-room.d.ts +40 -0
- package/dist/sync/partykit-room.d.ts.map +1 -0
- package/dist/sync/partykit-transport.d.ts +84 -0
- package/dist/sync/partykit-transport.d.ts.map +1 -0
- package/dist/sync/room-core.d.ts +53 -0
- package/dist/sync/room-core.d.ts.map +1 -0
- package/dist/sync/sync-engine.d.ts +40 -2
- package/dist/sync/sync-engine.d.ts.map +1 -1
- package/dist/sync/sync-room-server.d.ts +39 -0
- package/dist/sync/sync-room-server.d.ts.map +1 -0
- package/dist/sync/types.d.ts +70 -3
- package/dist/sync/types.d.ts.map +1 -1
- package/dist/sync/vcs-sync-peer.d.ts +63 -0
- package/dist/sync/vcs-sync-peer.d.ts.map +1 -0
- package/dist/sync/ws-transport.d.ts +2 -2
- package/dist/sync/ws-transport.d.ts.map +1 -1
- package/dist/tenancy-NBLLGCNJ.js +14 -0
- package/dist/tenancy-POMWQP6M.js +16 -0
- package/dist/ui/client.html +355 -67
- package/dist/ui/launch-explorer.d.ts +22 -0
- package/dist/ui/launch-explorer.d.ts.map +1 -0
- package/dist/ui/server.d.ts +1 -1
- package/dist/ui/server.d.ts.map +1 -1
- package/dist/vcs/blob-store.d.ts.map +1 -1
- package/dist/vcs/idb-op-log.d.ts +67 -0
- package/dist/vcs/idb-op-log.d.ts.map +1 -0
- package/dist/vcs/index.d.ts +1 -0
- package/dist/vcs/index.d.ts.map +1 -1
- package/dist/vcs/index.js +81 -72
- package/dist/vcs/issue.d.ts +22 -5
- package/dist/vcs/issue.d.ts.map +1 -1
- package/dist/vcs/lane-materialize.d.ts +5 -0
- package/dist/vcs/lane-materialize.d.ts.map +1 -1
- package/dist/vcs/op-log.d.ts +28 -2
- package/dist/vcs/op-log.d.ts.map +1 -1
- package/dist/vcs/ops.d.ts +10 -0
- package/dist/vcs/ops.d.ts.map +1 -1
- package/dist/vcs/types.d.ts +2 -0
- package/dist/vcs/types.d.ts.map +1 -1
- package/dist/{vm-config-6xhsj6b3.js → vm-config-JI4OXKDQ.js} +9 -10
- package/dist/vue/hooks.d.ts +64 -0
- package/dist/vue/hooks.d.ts.map +1 -0
- package/dist/vue/index.d.ts +19 -0
- package/dist/vue/index.d.ts.map +1 -0
- package/dist/vue/index.js +59 -0
- package/dist/vue/schema-hooks.d.ts +41 -0
- package/dist/vue/schema-hooks.d.ts.map +1 -0
- package/dist/vue/schema-hooks.js +64 -0
- package/package.json +130 -17
- package/dist/deploy-999q207z.js +0 -10
- package/dist/engine-y0724kjq.js +0 -8
- package/dist/import-s2b8e0ft.js +0 -11
- package/dist/index-a76rekgs.js +0 -67
- package/dist/index-c9h37r6h.js +0 -1
- package/dist/index-hr9qvv77.js +0 -860
- package/dist/index-k5b0xskw.js +0 -1
- package/dist/index-nzb3am4f.js +0 -80
- package/dist/index-wncptktd.js +0 -292
- package/dist/index-y6a4kj0p.js +0 -43
- package/dist/index-yhwjgfvj.js +0 -342
- package/dist/sdk-bepky0xs.js +0 -16
- package/dist/server-szdjx0nt.js +0 -13
- package/dist/sqlite-backend-0vsmc6qj.js +0 -8
- package/dist/tenancy-pjm32b4v.js +0 -14
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
import "../../chunk-KFQGP6VL.js";
|
|
2
|
+
|
|
3
|
+
// src/plugins/plan-approval/ontology.ts
|
|
4
|
+
var planApprovalOntology = {
|
|
5
|
+
id: "trellis:plan-approval",
|
|
6
|
+
name: "Plan Approval",
|
|
7
|
+
version: "1.0.0",
|
|
8
|
+
description: "Multi-turn planning with buffered mutations and user approval",
|
|
9
|
+
entities: [
|
|
10
|
+
{
|
|
11
|
+
name: "PendingPlan",
|
|
12
|
+
description: "A buffered set of graph mutations awaiting user approval",
|
|
13
|
+
attributes: [
|
|
14
|
+
{ name: "title", type: "string", required: true },
|
|
15
|
+
{ name: "description", type: "string" },
|
|
16
|
+
{
|
|
17
|
+
name: "status",
|
|
18
|
+
type: "string",
|
|
19
|
+
required: true,
|
|
20
|
+
enum: ["drafting", "submitted", "approved", "rejected"],
|
|
21
|
+
default: "drafting"
|
|
22
|
+
},
|
|
23
|
+
{ name: "submittedAt", type: "string", description: "ISO 8601 timestamp" },
|
|
24
|
+
{ name: "resolvedAt", type: "string", description: "ISO 8601 timestamp" },
|
|
25
|
+
{ name: "resolvedBy", type: "string", description: "Agent or user who approved/rejected" },
|
|
26
|
+
{ name: "rejectionReason", type: "string" },
|
|
27
|
+
{ name: "operationCount", type: "number", description: "Number of planned operations" }
|
|
28
|
+
]
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
name: "PlannedOperation",
|
|
32
|
+
description: "A single buffered graph mutation within a plan",
|
|
33
|
+
attributes: [
|
|
34
|
+
{
|
|
35
|
+
name: "kind",
|
|
36
|
+
type: "string",
|
|
37
|
+
required: true,
|
|
38
|
+
enum: ["createEntity", "updateEntity", "deleteEntity", "addLink", "removeLink"]
|
|
39
|
+
},
|
|
40
|
+
{ name: "entityId", type: "string", description: "Target entity ID" },
|
|
41
|
+
{ name: "entityType", type: "string", description: "Entity type (for createEntity)" },
|
|
42
|
+
{ name: "attributes", type: "any", description: "JSON-serialized attributes" },
|
|
43
|
+
{ name: "sourceId", type: "string", description: "Source entity (for link operations)" },
|
|
44
|
+
{ name: "targetId", type: "string", description: "Target entity (for link operations)" },
|
|
45
|
+
{ name: "linkAttribute", type: "string", description: "Link attribute name" },
|
|
46
|
+
{ name: "description", type: "string", description: "Human-readable description of this operation" },
|
|
47
|
+
{ name: "sequence", type: "number", description: "Execution order within the plan" }
|
|
48
|
+
]
|
|
49
|
+
}
|
|
50
|
+
],
|
|
51
|
+
relations: [
|
|
52
|
+
{
|
|
53
|
+
name: "hasOperation",
|
|
54
|
+
sourceTypes: ["PendingPlan"],
|
|
55
|
+
targetTypes: ["PlannedOperation"],
|
|
56
|
+
cardinality: "many"
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
name: "planForRun",
|
|
60
|
+
sourceTypes: ["PendingPlan"],
|
|
61
|
+
targetTypes: ["AgentRun"],
|
|
62
|
+
cardinality: "one",
|
|
63
|
+
description: "Links a plan to the agent run that produced it"
|
|
64
|
+
}
|
|
65
|
+
]
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// src/plugins/plan-approval/plugin.ts
|
|
69
|
+
function createPlanApprovalPlugin() {
|
|
70
|
+
return {
|
|
71
|
+
id: "trellis:plan-approval",
|
|
72
|
+
name: "Plan Approval",
|
|
73
|
+
version: "1.0.0",
|
|
74
|
+
description: "Multi-turn planning with buffered mutations and user approval",
|
|
75
|
+
ontologies: [planApprovalOntology],
|
|
76
|
+
eventHandlers: [
|
|
77
|
+
{
|
|
78
|
+
event: "entity:updated",
|
|
79
|
+
handler: (data) => {
|
|
80
|
+
if (!data || typeof data !== "object") return;
|
|
81
|
+
const d = data;
|
|
82
|
+
if (d.type !== "PendingPlan") return;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
],
|
|
86
|
+
onLoad: async (ctx) => {
|
|
87
|
+
ctx.log("Plan approval system loaded");
|
|
88
|
+
},
|
|
89
|
+
onUnload: async (ctx) => {
|
|
90
|
+
ctx.log("Plan approval system unloaded");
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function createEnterPlanModeTool(ctx) {
|
|
95
|
+
return {
|
|
96
|
+
def: {
|
|
97
|
+
name: "enterPlanMode",
|
|
98
|
+
description: "Enter plan mode. Subsequent planCreateEntity/planUpdateEntity/planAddLink calls will be buffered for user review instead of executing immediately. Use this before performing bulk operations or significant architectural changes.",
|
|
99
|
+
schema: JSON.stringify({
|
|
100
|
+
type: "object",
|
|
101
|
+
properties: {
|
|
102
|
+
title: { type: "string", description: "Short title describing the planned changes" },
|
|
103
|
+
description: { type: "string", description: "Detailed description of what will be changed and why" }
|
|
104
|
+
},
|
|
105
|
+
required: ["title"]
|
|
106
|
+
})
|
|
107
|
+
},
|
|
108
|
+
handler: async (input) => {
|
|
109
|
+
const { title, description } = input;
|
|
110
|
+
const planId = await ctx.planManager.enterPlanMode(title, description);
|
|
111
|
+
return {
|
|
112
|
+
success: true,
|
|
113
|
+
output: { planId, message: `Plan mode active. Use planCreateEntity/planUpdateEntity/planAddLink to buffer operations, then submitPlan to send for review.` }
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
function createPlanCreateEntityTool(ctx) {
|
|
119
|
+
return {
|
|
120
|
+
def: {
|
|
121
|
+
name: "planCreateEntity",
|
|
122
|
+
description: "Buffer a new entity creation in the active plan. Must be in plan mode.",
|
|
123
|
+
schema: JSON.stringify({
|
|
124
|
+
type: "object",
|
|
125
|
+
properties: {
|
|
126
|
+
entityId: { type: "string", description: "Unique entity ID" },
|
|
127
|
+
entityType: { type: "string", description: "Entity type name" },
|
|
128
|
+
attributes: { type: "object", description: "Entity attributes as key-value pairs" },
|
|
129
|
+
description: { type: "string", description: "Human-readable description of this operation" }
|
|
130
|
+
},
|
|
131
|
+
required: ["entityId", "entityType"]
|
|
132
|
+
})
|
|
133
|
+
},
|
|
134
|
+
handler: async (input) => {
|
|
135
|
+
const { entityId, entityType, attributes, description } = input;
|
|
136
|
+
const opId = await ctx.planManager.planCreateEntity(entityId, entityType, attributes, description);
|
|
137
|
+
return { success: true, output: { operationId: opId } };
|
|
138
|
+
}
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
function createPlanUpdateEntityTool(ctx) {
|
|
142
|
+
return {
|
|
143
|
+
def: {
|
|
144
|
+
name: "planUpdateEntity",
|
|
145
|
+
description: "Buffer an entity update in the active plan. Must be in plan mode.",
|
|
146
|
+
schema: JSON.stringify({
|
|
147
|
+
type: "object",
|
|
148
|
+
properties: {
|
|
149
|
+
entityId: { type: "string", description: "Target entity ID" },
|
|
150
|
+
attributes: { type: "object", description: "Attributes to update" },
|
|
151
|
+
description: { type: "string", description: "Human-readable description" }
|
|
152
|
+
},
|
|
153
|
+
required: ["entityId", "attributes"]
|
|
154
|
+
})
|
|
155
|
+
},
|
|
156
|
+
handler: async (input) => {
|
|
157
|
+
const { entityId, attributes, description } = input;
|
|
158
|
+
const opId = await ctx.planManager.planUpdateEntity(entityId, attributes, description);
|
|
159
|
+
return { success: true, output: { operationId: opId } };
|
|
160
|
+
}
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
function createPlanAddLinkTool(ctx) {
|
|
164
|
+
return {
|
|
165
|
+
def: {
|
|
166
|
+
name: "planAddLink",
|
|
167
|
+
description: "Buffer a link creation in the active plan. Must be in plan mode.",
|
|
168
|
+
schema: JSON.stringify({
|
|
169
|
+
type: "object",
|
|
170
|
+
properties: {
|
|
171
|
+
sourceId: { type: "string", description: "Source entity ID" },
|
|
172
|
+
linkAttribute: { type: "string", description: "Relationship attribute name" },
|
|
173
|
+
targetId: { type: "string", description: "Target entity ID" },
|
|
174
|
+
description: { type: "string", description: "Human-readable description" }
|
|
175
|
+
},
|
|
176
|
+
required: ["sourceId", "linkAttribute", "targetId"]
|
|
177
|
+
})
|
|
178
|
+
},
|
|
179
|
+
handler: async (input) => {
|
|
180
|
+
const { sourceId, linkAttribute, targetId, description } = input;
|
|
181
|
+
const opId = await ctx.planManager.planAddLink(sourceId, linkAttribute, targetId, description);
|
|
182
|
+
return { success: true, output: { operationId: opId } };
|
|
183
|
+
}
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function createSubmitPlanTool(ctx) {
|
|
187
|
+
return {
|
|
188
|
+
def: {
|
|
189
|
+
name: "submitPlan",
|
|
190
|
+
description: "Submit the active plan for user review. The plan will be presented to the user for approval before any operations are executed. The agent loop will pause until the user approves or rejects.",
|
|
191
|
+
schema: JSON.stringify({
|
|
192
|
+
type: "object",
|
|
193
|
+
properties: {}
|
|
194
|
+
})
|
|
195
|
+
},
|
|
196
|
+
handler: async () => {
|
|
197
|
+
const plan = await ctx.planManager.submitPlan();
|
|
198
|
+
return {
|
|
199
|
+
success: true,
|
|
200
|
+
output: {
|
|
201
|
+
_planPending: true,
|
|
202
|
+
planId: plan.id,
|
|
203
|
+
title: plan.title,
|
|
204
|
+
description: plan.description,
|
|
205
|
+
operationCount: plan.operations.length,
|
|
206
|
+
operations: plan.operations.map((op) => ({
|
|
207
|
+
kind: op.kind,
|
|
208
|
+
description: op.description,
|
|
209
|
+
entityId: op.entityId,
|
|
210
|
+
entityType: op.entityType
|
|
211
|
+
}))
|
|
212
|
+
}
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
function createCancelPlanTool(ctx) {
|
|
218
|
+
return {
|
|
219
|
+
def: {
|
|
220
|
+
name: "cancelPlan",
|
|
221
|
+
description: "Cancel the active plan without submitting. Discards all buffered operations.",
|
|
222
|
+
schema: JSON.stringify({
|
|
223
|
+
type: "object",
|
|
224
|
+
properties: {}
|
|
225
|
+
})
|
|
226
|
+
},
|
|
227
|
+
handler: async () => {
|
|
228
|
+
await ctx.planManager.cancelPlan();
|
|
229
|
+
return { success: true, output: { message: "Plan cancelled." } };
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
function registerPlanTools(harness, ctx) {
|
|
234
|
+
const tools = [
|
|
235
|
+
createEnterPlanModeTool(ctx),
|
|
236
|
+
createPlanCreateEntityTool(ctx),
|
|
237
|
+
createPlanUpdateEntityTool(ctx),
|
|
238
|
+
createPlanAddLinkTool(ctx),
|
|
239
|
+
createSubmitPlanTool(ctx),
|
|
240
|
+
createCancelPlanTool(ctx)
|
|
241
|
+
];
|
|
242
|
+
return Promise.all(
|
|
243
|
+
tools.map((t) => harness.registerTool(t.def, t.handler))
|
|
244
|
+
);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
// src/plugins/plan-approval/plan-manager.ts
|
|
248
|
+
var planIdCounter = 0;
|
|
249
|
+
var PlanManager = class {
|
|
250
|
+
kernel;
|
|
251
|
+
activePlan = null;
|
|
252
|
+
constructor(kernel) {
|
|
253
|
+
this.kernel = kernel;
|
|
254
|
+
}
|
|
255
|
+
// -------------------------------------------------------------------------
|
|
256
|
+
// Plan mode lifecycle
|
|
257
|
+
// -------------------------------------------------------------------------
|
|
258
|
+
/**
|
|
259
|
+
* Enter plan mode. Creates a PendingPlan entity in the graph and sets it
|
|
260
|
+
* as the active plan. All subsequent `addOperation()` calls buffer into it.
|
|
261
|
+
*/
|
|
262
|
+
async enterPlanMode(title, description) {
|
|
263
|
+
if (this.activePlan && this.activePlan.status === "drafting") {
|
|
264
|
+
throw new Error("PlanManager: Already in plan mode. Submit or cancel the current plan first.");
|
|
265
|
+
}
|
|
266
|
+
const id = `plan:${Date.now()}:${++planIdCounter}`;
|
|
267
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
268
|
+
const plan = {
|
|
269
|
+
id,
|
|
270
|
+
status: "drafting",
|
|
271
|
+
title,
|
|
272
|
+
description,
|
|
273
|
+
operations: [],
|
|
274
|
+
createdAt: now
|
|
275
|
+
};
|
|
276
|
+
const attrs = {
|
|
277
|
+
title,
|
|
278
|
+
status: "drafting"
|
|
279
|
+
};
|
|
280
|
+
if (description) attrs.description = description;
|
|
281
|
+
attrs.operationCount = 0;
|
|
282
|
+
await this.kernel.createEntity(id, "PendingPlan", attrs);
|
|
283
|
+
this.activePlan = plan;
|
|
284
|
+
return id;
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* Whether a plan is currently being drafted.
|
|
288
|
+
*/
|
|
289
|
+
isInPlanMode() {
|
|
290
|
+
return this.activePlan !== null && this.activePlan.status === "drafting";
|
|
291
|
+
}
|
|
292
|
+
/**
|
|
293
|
+
* Get the active plan (if any).
|
|
294
|
+
*/
|
|
295
|
+
getActivePlan() {
|
|
296
|
+
return this.activePlan;
|
|
297
|
+
}
|
|
298
|
+
// -------------------------------------------------------------------------
|
|
299
|
+
// Buffer operations
|
|
300
|
+
// -------------------------------------------------------------------------
|
|
301
|
+
/**
|
|
302
|
+
* Buffer a planned graph operation. Only valid in plan mode.
|
|
303
|
+
* Returns the operation ID.
|
|
304
|
+
*/
|
|
305
|
+
async addOperation(op) {
|
|
306
|
+
if (!this.activePlan || this.activePlan.status !== "drafting") {
|
|
307
|
+
throw new Error("PlanManager: Not in plan mode. Call enterPlanMode() first.");
|
|
308
|
+
}
|
|
309
|
+
const sequence = this.activePlan.operations.length;
|
|
310
|
+
const opId = `${this.activePlan.id}:op:${sequence}`;
|
|
311
|
+
const planned = {
|
|
312
|
+
...op,
|
|
313
|
+
id: opId,
|
|
314
|
+
sequence
|
|
315
|
+
};
|
|
316
|
+
this.activePlan.operations.push(planned);
|
|
317
|
+
const attrs = {
|
|
318
|
+
kind: op.kind,
|
|
319
|
+
sequence
|
|
320
|
+
};
|
|
321
|
+
if (op.entityId) attrs.entityId = op.entityId;
|
|
322
|
+
if (op.entityType) attrs.entityType = op.entityType;
|
|
323
|
+
if (op.attributes) attrs.attributes = JSON.stringify(op.attributes);
|
|
324
|
+
if (op.sourceId) attrs.sourceId = op.sourceId;
|
|
325
|
+
if (op.targetId) attrs.targetId = op.targetId;
|
|
326
|
+
if (op.linkAttribute) attrs.linkAttribute = op.linkAttribute;
|
|
327
|
+
if (op.description) attrs.description = op.description;
|
|
328
|
+
await this.kernel.createEntity(opId, "PlannedOperation", attrs);
|
|
329
|
+
await this.kernel.addLink(this.activePlan.id, "hasOperation", opId);
|
|
330
|
+
await this.kernel.updateEntity(this.activePlan.id, {
|
|
331
|
+
operationCount: this.activePlan.operations.length
|
|
332
|
+
});
|
|
333
|
+
return opId;
|
|
334
|
+
}
|
|
335
|
+
// -------------------------------------------------------------------------
|
|
336
|
+
// Convenience buffers (mirror the kernel entity API)
|
|
337
|
+
// -------------------------------------------------------------------------
|
|
338
|
+
async planCreateEntity(entityId, entityType, attributes, description) {
|
|
339
|
+
return this.addOperation({
|
|
340
|
+
kind: "createEntity",
|
|
341
|
+
entityId,
|
|
342
|
+
entityType,
|
|
343
|
+
attributes,
|
|
344
|
+
description: description ?? `Create ${entityType} "${entityId}"`
|
|
345
|
+
});
|
|
346
|
+
}
|
|
347
|
+
async planUpdateEntity(entityId, attributes, description) {
|
|
348
|
+
return this.addOperation({
|
|
349
|
+
kind: "updateEntity",
|
|
350
|
+
entityId,
|
|
351
|
+
attributes,
|
|
352
|
+
description: description ?? `Update "${entityId}"`
|
|
353
|
+
});
|
|
354
|
+
}
|
|
355
|
+
async planDeleteEntity(entityId, description) {
|
|
356
|
+
return this.addOperation({
|
|
357
|
+
kind: "deleteEntity",
|
|
358
|
+
entityId,
|
|
359
|
+
description: description ?? `Delete "${entityId}"`
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
async planAddLink(sourceId, linkAttribute, targetId, description) {
|
|
363
|
+
return this.addOperation({
|
|
364
|
+
kind: "addLink",
|
|
365
|
+
sourceId,
|
|
366
|
+
targetId,
|
|
367
|
+
linkAttribute,
|
|
368
|
+
description: description ?? `Link ${sourceId} -[${linkAttribute}]-> ${targetId}`
|
|
369
|
+
});
|
|
370
|
+
}
|
|
371
|
+
async planRemoveLink(sourceId, linkAttribute, targetId, description) {
|
|
372
|
+
return this.addOperation({
|
|
373
|
+
kind: "removeLink",
|
|
374
|
+
sourceId,
|
|
375
|
+
targetId,
|
|
376
|
+
linkAttribute,
|
|
377
|
+
description: description ?? `Unlink ${sourceId} -[${linkAttribute}]-> ${targetId}`
|
|
378
|
+
});
|
|
379
|
+
}
|
|
380
|
+
// -------------------------------------------------------------------------
|
|
381
|
+
// Plan resolution
|
|
382
|
+
// -------------------------------------------------------------------------
|
|
383
|
+
/**
|
|
384
|
+
* Submit the active plan for review. Marks it as submitted and returns
|
|
385
|
+
* the plan for the caller to present to the user.
|
|
386
|
+
*/
|
|
387
|
+
async submitPlan() {
|
|
388
|
+
if (!this.activePlan || this.activePlan.status !== "drafting") {
|
|
389
|
+
throw new Error("PlanManager: No active plan to submit.");
|
|
390
|
+
}
|
|
391
|
+
if (this.activePlan.operations.length === 0) {
|
|
392
|
+
throw new Error("PlanManager: Cannot submit an empty plan.");
|
|
393
|
+
}
|
|
394
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
395
|
+
this.activePlan.status = "submitted";
|
|
396
|
+
this.activePlan.submittedAt = now;
|
|
397
|
+
await this.kernel.updateEntity(this.activePlan.id, {
|
|
398
|
+
status: "submitted",
|
|
399
|
+
submittedAt: now
|
|
400
|
+
});
|
|
401
|
+
return { ...this.activePlan };
|
|
402
|
+
}
|
|
403
|
+
/**
|
|
404
|
+
* Approve a submitted plan. Replays all buffered operations against the
|
|
405
|
+
* real kernel in sequence order. Returns the results of each operation.
|
|
406
|
+
*/
|
|
407
|
+
async approvePlan(resolvedBy) {
|
|
408
|
+
if (!this.activePlan || this.activePlan.status !== "submitted") {
|
|
409
|
+
throw new Error("PlanManager: No submitted plan to approve.");
|
|
410
|
+
}
|
|
411
|
+
const plan = this.activePlan;
|
|
412
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
413
|
+
const results = [];
|
|
414
|
+
const ops = [...plan.operations].sort((a, b) => a.sequence - b.sequence);
|
|
415
|
+
for (const op of ops) {
|
|
416
|
+
const result = await this._executeOperation(op);
|
|
417
|
+
if (result) results.push(result);
|
|
418
|
+
}
|
|
419
|
+
plan.status = "approved";
|
|
420
|
+
plan.resolvedAt = now;
|
|
421
|
+
plan.resolvedBy = resolvedBy;
|
|
422
|
+
await this.kernel.updateEntity(plan.id, {
|
|
423
|
+
status: "approved",
|
|
424
|
+
resolvedAt: now,
|
|
425
|
+
...resolvedBy ? { resolvedBy } : {}
|
|
426
|
+
});
|
|
427
|
+
this.activePlan = null;
|
|
428
|
+
return {
|
|
429
|
+
planId: plan.id,
|
|
430
|
+
operationsExecuted: results.length,
|
|
431
|
+
results
|
|
432
|
+
};
|
|
433
|
+
}
|
|
434
|
+
/**
|
|
435
|
+
* Reject a submitted plan. The plan and its operations remain in the graph
|
|
436
|
+
* as entities, preserving them as substrate for the Idea Garden (Phase 6).
|
|
437
|
+
*/
|
|
438
|
+
async rejectPlan(reason, resolvedBy) {
|
|
439
|
+
if (!this.activePlan || this.activePlan.status !== "submitted") {
|
|
440
|
+
throw new Error("PlanManager: No submitted plan to reject.");
|
|
441
|
+
}
|
|
442
|
+
const now = (/* @__PURE__ */ new Date()).toISOString();
|
|
443
|
+
this.activePlan.status = "rejected";
|
|
444
|
+
this.activePlan.resolvedAt = now;
|
|
445
|
+
this.activePlan.resolvedBy = resolvedBy;
|
|
446
|
+
this.activePlan.rejectionReason = reason;
|
|
447
|
+
const updates = {
|
|
448
|
+
status: "rejected",
|
|
449
|
+
resolvedAt: now
|
|
450
|
+
};
|
|
451
|
+
if (reason) updates.rejectionReason = reason;
|
|
452
|
+
if (resolvedBy) updates.resolvedBy = resolvedBy;
|
|
453
|
+
await this.kernel.updateEntity(this.activePlan.id, updates);
|
|
454
|
+
this.activePlan = null;
|
|
455
|
+
}
|
|
456
|
+
/**
|
|
457
|
+
* Cancel a drafting plan without submitting. Like rejection but for
|
|
458
|
+
* plans that were never submitted.
|
|
459
|
+
*/
|
|
460
|
+
async cancelPlan() {
|
|
461
|
+
if (!this.activePlan || this.activePlan.status !== "drafting") {
|
|
462
|
+
throw new Error("PlanManager: No active plan to cancel.");
|
|
463
|
+
}
|
|
464
|
+
await this.kernel.updateEntity(this.activePlan.id, {
|
|
465
|
+
status: "rejected",
|
|
466
|
+
resolvedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
467
|
+
rejectionReason: "Cancelled before submission"
|
|
468
|
+
});
|
|
469
|
+
this.activePlan = null;
|
|
470
|
+
}
|
|
471
|
+
// -------------------------------------------------------------------------
|
|
472
|
+
// Query helpers
|
|
473
|
+
// -------------------------------------------------------------------------
|
|
474
|
+
/**
|
|
475
|
+
* Load a plan from the graph by ID, including its operations.
|
|
476
|
+
*/
|
|
477
|
+
loadPlan(planId) {
|
|
478
|
+
const entity = this.kernel.getEntity(planId);
|
|
479
|
+
if (!entity || entity.type !== "PendingPlan") return null;
|
|
480
|
+
const get = (a) => entity.facts.find((f) => f.a === a)?.v;
|
|
481
|
+
const store = this.kernel.getStore();
|
|
482
|
+
const opLinks = store.getLinksByEntityAndAttribute(planId, "hasOperation");
|
|
483
|
+
const operations = [];
|
|
484
|
+
for (const link of opLinks) {
|
|
485
|
+
const opEntity = this.kernel.getEntity(link.e2);
|
|
486
|
+
if (!opEntity || opEntity.type !== "PlannedOperation") continue;
|
|
487
|
+
const opGet = (a) => opEntity.facts.find((f) => f.a === a)?.v;
|
|
488
|
+
let attributes;
|
|
489
|
+
const attrsRaw = opGet("attributes");
|
|
490
|
+
if (attrsRaw) {
|
|
491
|
+
try {
|
|
492
|
+
attributes = JSON.parse(attrsRaw);
|
|
493
|
+
} catch {
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
operations.push({
|
|
497
|
+
id: opEntity.id,
|
|
498
|
+
kind: opGet("kind"),
|
|
499
|
+
entityId: opGet("entityId"),
|
|
500
|
+
entityType: opGet("entityType"),
|
|
501
|
+
attributes,
|
|
502
|
+
sourceId: opGet("sourceId"),
|
|
503
|
+
targetId: opGet("targetId"),
|
|
504
|
+
linkAttribute: opGet("linkAttribute"),
|
|
505
|
+
description: opGet("description"),
|
|
506
|
+
sequence: opGet("sequence") ?? 0
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
operations.sort((a, b) => a.sequence - b.sequence);
|
|
510
|
+
return {
|
|
511
|
+
id: planId,
|
|
512
|
+
status: get("status") ?? "drafting",
|
|
513
|
+
title: String(get("title") ?? ""),
|
|
514
|
+
description: get("description"),
|
|
515
|
+
operations,
|
|
516
|
+
createdAt: String(get("createdAt") ?? ""),
|
|
517
|
+
submittedAt: get("submittedAt"),
|
|
518
|
+
resolvedAt: get("resolvedAt"),
|
|
519
|
+
resolvedBy: get("resolvedBy"),
|
|
520
|
+
rejectionReason: get("rejectionReason")
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
/**
|
|
524
|
+
* List all plans, optionally filtered by status.
|
|
525
|
+
*/
|
|
526
|
+
listPlans(status) {
|
|
527
|
+
const entities = this.kernel.listEntities("PendingPlan", status ? { status } : void 0);
|
|
528
|
+
return entities.map((e) => this.loadPlan(e.id)).filter((p) => p !== null);
|
|
529
|
+
}
|
|
530
|
+
// -------------------------------------------------------------------------
|
|
531
|
+
// Internal
|
|
532
|
+
// -------------------------------------------------------------------------
|
|
533
|
+
/**
|
|
534
|
+
* Execute a single planned operation against the real kernel.
|
|
535
|
+
*/
|
|
536
|
+
async _executeOperation(op) {
|
|
537
|
+
switch (op.kind) {
|
|
538
|
+
case "createEntity":
|
|
539
|
+
if (!op.entityId || !op.entityType) {
|
|
540
|
+
throw new Error(`PlannedOperation ${op.id}: createEntity requires entityId and entityType`);
|
|
541
|
+
}
|
|
542
|
+
return this.kernel.createEntity(op.entityId, op.entityType, op.attributes ?? {});
|
|
543
|
+
case "updateEntity":
|
|
544
|
+
if (!op.entityId || !op.attributes) {
|
|
545
|
+
throw new Error(`PlannedOperation ${op.id}: updateEntity requires entityId and attributes`);
|
|
546
|
+
}
|
|
547
|
+
return this.kernel.updateEntity(op.entityId, op.attributes);
|
|
548
|
+
case "deleteEntity":
|
|
549
|
+
if (!op.entityId) {
|
|
550
|
+
throw new Error(`PlannedOperation ${op.id}: deleteEntity requires entityId`);
|
|
551
|
+
}
|
|
552
|
+
return this.kernel.deleteEntity(op.entityId);
|
|
553
|
+
case "addLink":
|
|
554
|
+
if (!op.sourceId || !op.linkAttribute || !op.targetId) {
|
|
555
|
+
throw new Error(`PlannedOperation ${op.id}: addLink requires sourceId, linkAttribute, and targetId`);
|
|
556
|
+
}
|
|
557
|
+
return this.kernel.addLink(op.sourceId, op.linkAttribute, op.targetId);
|
|
558
|
+
case "removeLink":
|
|
559
|
+
if (!op.sourceId || !op.linkAttribute || !op.targetId) {
|
|
560
|
+
throw new Error(`PlannedOperation ${op.id}: removeLink requires sourceId, linkAttribute, and targetId`);
|
|
561
|
+
}
|
|
562
|
+
return this.kernel.removeLink(op.sourceId, op.linkAttribute, op.targetId);
|
|
563
|
+
default:
|
|
564
|
+
throw new Error(`PlannedOperation ${op.id}: Unknown operation kind "${op.kind}"`);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
};
|
|
568
|
+
export {
|
|
569
|
+
PlanManager,
|
|
570
|
+
createCancelPlanTool,
|
|
571
|
+
createEnterPlanModeTool,
|
|
572
|
+
createPlanAddLinkTool,
|
|
573
|
+
createPlanApprovalPlugin,
|
|
574
|
+
createPlanCreateEntityTool,
|
|
575
|
+
createPlanUpdateEntityTool,
|
|
576
|
+
createSubmitPlanTool,
|
|
577
|
+
planApprovalOntology,
|
|
578
|
+
registerPlanTools
|
|
579
|
+
};
|