@honua/mcp-server 0.2.3 → 0.2.4
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 +63 -0
- package/dist/src/agent/cli.d.ts +3 -0
- package/dist/src/agent/cli.d.ts.map +1 -0
- package/dist/src/agent/cli.js +33 -0
- package/dist/src/agent/devops-configuration.d.ts +5 -0
- package/dist/src/agent/devops-configuration.d.ts.map +1 -0
- package/dist/src/agent/devops-configuration.js +130 -0
- package/dist/src/agent/devops-runtime.d.ts +42 -0
- package/dist/src/agent/devops-runtime.d.ts.map +1 -0
- package/dist/src/agent/devops-runtime.js +242 -0
- package/dist/src/agent/devops-targets.d.ts +33 -0
- package/dist/src/agent/devops-targets.d.ts.map +1 -0
- package/dist/src/agent/devops-targets.js +25 -0
- package/dist/src/agent/gateway.d.ts +58 -0
- package/dist/src/agent/gateway.d.ts.map +1 -0
- package/dist/src/agent/gateway.js +381 -0
- package/dist/src/agent/local-runtime.d.ts +26 -0
- package/dist/src/agent/local-runtime.d.ts.map +1 -0
- package/dist/src/agent/local-runtime.js +152 -0
- package/dist/src/agent/native-cli.d.ts +4 -0
- package/dist/src/agent/native-cli.d.ts.map +1 -0
- package/dist/src/agent/native-cli.js +49 -0
- package/dist/src/agent/native-http.d.ts +18 -0
- package/dist/src/agent/native-http.d.ts.map +1 -0
- package/dist/src/agent/native-http.js +261 -0
- package/dist/src/agent/workflow-knowledge.d.ts +73 -0
- package/dist/src/agent/workflow-knowledge.d.ts.map +1 -0
- package/dist/src/agent/workflow-knowledge.js +230 -0
- package/package.json +4 -2
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { isMainEntrypoint } from "../entrypoint.js";
|
|
5
|
+
import { createLocalAgentRuntime } from "./local-runtime.js";
|
|
6
|
+
import { createNativeAgentHttpServer } from "./native-http.js";
|
|
7
|
+
/** Host-only native bridge. The browser continues to use Console's authenticated BFF. */
|
|
8
|
+
export async function runNativeAgentGateway(env = process.env) {
|
|
9
|
+
const port = Number(env.HONUA_AGENT_GATEWAY_PORT ?? "7863");
|
|
10
|
+
const host = env.HONUA_AGENT_GATEWAY_HOST ?? "127.0.0.1";
|
|
11
|
+
if (!Number.isInteger(port) || port < 1 || port > 65535)
|
|
12
|
+
throw new Error("Invalid gateway port");
|
|
13
|
+
if (!["127.0.0.1", "::1", "0.0.0.0"].includes(host))
|
|
14
|
+
throw new Error("Gateway host must be an explicit listen address");
|
|
15
|
+
const server = createNativeAgentHttpServer({
|
|
16
|
+
runtime: createLocalAgentRuntime(env.HONUA_AGENT_INSTALL_DIRECTORY ?? path.join(homedir(), ".honua", "local")),
|
|
17
|
+
bridgeKey: env.HONUA_AGENT_GATEWAY_KEY ?? "",
|
|
18
|
+
...(env.HONUA_AGENT_DEVOPS_CONFIG ? { devopsConfiguration: env.HONUA_AGENT_DEVOPS_CONFIG } : {}),
|
|
19
|
+
});
|
|
20
|
+
await new Promise((resolve, reject) => {
|
|
21
|
+
server.once("error", reject);
|
|
22
|
+
server.listen(port, host, () => {
|
|
23
|
+
server.removeListener("error", reject);
|
|
24
|
+
resolve();
|
|
25
|
+
});
|
|
26
|
+
});
|
|
27
|
+
const close = () => {
|
|
28
|
+
server.closeAllConnections();
|
|
29
|
+
server.close();
|
|
30
|
+
};
|
|
31
|
+
process.once("SIGINT", close);
|
|
32
|
+
process.once("SIGTERM", close);
|
|
33
|
+
server.once("close", () => {
|
|
34
|
+
process.removeListener("SIGINT", close);
|
|
35
|
+
process.removeListener("SIGTERM", close);
|
|
36
|
+
});
|
|
37
|
+
return server;
|
|
38
|
+
}
|
|
39
|
+
if (isMainEntrypoint(import.meta.url)) {
|
|
40
|
+
if (process.argv.includes("--help")) {
|
|
41
|
+
process.stdout.write("honua-agent-gateway — authenticated native HTTP bridge over the unified Honua MCP gateway.\nConfigure HONUA_AGENT_INSTALL_DIRECTORY and HONUA_AGENT_GATEWAY_KEY on the host. Default listen address: 127.0.0.1:7863.\n");
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
runNativeAgentGateway().catch(() => {
|
|
45
|
+
process.stderr.write("Native Honua gateway failed; inspect host configuration.\n");
|
|
46
|
+
process.exitCode = 1;
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type IncomingMessage, type ServerResponse } from "node:http";
|
|
2
|
+
import type { AgentRuntime } from "./local-runtime.js";
|
|
3
|
+
export interface NativeGatewayOptions {
|
|
4
|
+
runtime: AgentRuntime;
|
|
5
|
+
/** Independent backend bridge credential. Never supplied by a browser or a model. */
|
|
6
|
+
bridgeKey: string;
|
|
7
|
+
devopsConfiguration?: string;
|
|
8
|
+
fetchImpl?: typeof fetch;
|
|
9
|
+
maxSessions?: number;
|
|
10
|
+
idleTimeoutMs?: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Backend-only HTTP transport over the same gateway used by external agents.
|
|
14
|
+
* Sessions pin identity/workspace; AsyncLocalStorage pins each concurrent request's turn.
|
|
15
|
+
* Every request rereads the captured turn under the actual agent credential.
|
|
16
|
+
*/
|
|
17
|
+
export declare function createNativeAgentHttpServer(options: NativeGatewayOptions): import("http").Server<typeof IncomingMessage, typeof ServerResponse>;
|
|
18
|
+
//# sourceMappingURL=native-http.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"native-http.d.ts","sourceRoot":"","sources":["../../../src/agent/native-http.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,cAAc,EAAgB,MAAM,WAAW,CAAC;AAOpF,OAAO,KAAK,EAAmB,YAAY,EAAE,MAAM,oBAAoB,CAAC;AAUxE,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,YAAY,CAAC;IACtB,qFAAqF;IACrF,SAAS,EAAE,MAAM,CAAC;IAClB,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;IACzB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAyCD;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,EAAE,oBAAoB,wEAgOxE"}
|
|
@@ -0,0 +1,261 @@
|
|
|
1
|
+
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
|
+
import { randomUUID, timingSafeEqual } from "node:crypto";
|
|
3
|
+
import { createServer } from "node:http";
|
|
4
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
5
|
+
import { InitializeRequestSchema } from "@modelcontextprotocol/sdk/types.js";
|
|
6
|
+
import { buildUpstreamHeaders, readWorkflowView } from "../proxy.js";
|
|
7
|
+
import { configuredDevOps } from "./devops-configuration.js";
|
|
8
|
+
import { DEVOPS_TOOL_FAMILY } from "./devops-runtime.js";
|
|
9
|
+
import { HonuaAgentGateway, connectAgentUpstream } from "./gateway.js";
|
|
10
|
+
class Refusal extends Error {
|
|
11
|
+
status;
|
|
12
|
+
constructor(status) {
|
|
13
|
+
super("Native gateway request refused");
|
|
14
|
+
this.status = status;
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
function header(request, name, optional = false) {
|
|
18
|
+
const values = request.rawHeaders.filter((_value, index) => index % 2 === 0 && request.rawHeaders[index]?.toLowerCase() === name);
|
|
19
|
+
const value = request.headers[name];
|
|
20
|
+
if (optional && values.length === 0)
|
|
21
|
+
return "";
|
|
22
|
+
if (values.length !== 1 || typeof value !== "string" || !value || value.length > 8192)
|
|
23
|
+
throw new Refusal(400);
|
|
24
|
+
return value;
|
|
25
|
+
}
|
|
26
|
+
function identifier(value) {
|
|
27
|
+
if (!/^[A-Za-z0-9_-]{1,128}$/.test(value))
|
|
28
|
+
throw new Refusal(400);
|
|
29
|
+
return value;
|
|
30
|
+
}
|
|
31
|
+
function sameSecret(left, right) {
|
|
32
|
+
const supplied = Buffer.from(left, "utf8");
|
|
33
|
+
const expected = Buffer.from(right, "utf8");
|
|
34
|
+
return supplied.length === expected.length && timingSafeEqual(supplied, expected);
|
|
35
|
+
}
|
|
36
|
+
function samePrincipal(expected, current) {
|
|
37
|
+
return (expected.workspaceId === current.workspaceId &&
|
|
38
|
+
expected.targetId === current.targetId &&
|
|
39
|
+
expected.partition === current.partition &&
|
|
40
|
+
expected.connection.target.id === current.connection.target.id &&
|
|
41
|
+
expected.connection.target.credentialId === current.connection.target.credentialId &&
|
|
42
|
+
expected.connection.transport.remoteUrl === current.connection.transport.remoteUrl &&
|
|
43
|
+
sameSecret(expected.connection.transport.apiKey ?? "", current.connection.transport.apiKey ?? ""));
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Backend-only HTTP transport over the same gateway used by external agents.
|
|
47
|
+
* Sessions pin identity/workspace; AsyncLocalStorage pins each concurrent request's turn.
|
|
48
|
+
* Every request rereads the captured turn under the actual agent credential.
|
|
49
|
+
*/
|
|
50
|
+
export function createNativeAgentHttpServer(options) {
|
|
51
|
+
if (Buffer.byteLength(options.bridgeKey) < 32)
|
|
52
|
+
throw new Error("A separate bridge key of at least 32 bytes is required");
|
|
53
|
+
const network = options.fetchImpl ?? fetch;
|
|
54
|
+
const bindings = new AsyncLocalStorage();
|
|
55
|
+
const sessions = new Map();
|
|
56
|
+
let initializing = 0;
|
|
57
|
+
const maximum = options.maxSessions ?? 32;
|
|
58
|
+
const idle = options.idleTimeoutMs ?? 600_000;
|
|
59
|
+
if (!Number.isInteger(maximum) || maximum < 1 || maximum > 128 || !Number.isFinite(idle) || idle < 1000)
|
|
60
|
+
throw new Error("Invalid native gateway session limits");
|
|
61
|
+
async function authorize(request) {
|
|
62
|
+
if (request.headers.origin || request.headers.authorization)
|
|
63
|
+
throw new Refusal(403);
|
|
64
|
+
if (!sameSecret(header(request, "x-honua-gateway-key"), options.bridgeKey))
|
|
65
|
+
throw new Refusal(403);
|
|
66
|
+
const workspaceId = identifier(header(request, "x-honua-workspace"));
|
|
67
|
+
const turnId = identifier(header(request, "x-honua-turn"));
|
|
68
|
+
const partition = identifier(header(request, "x-honua-operator-partition"));
|
|
69
|
+
const supplied = header(request, "x-api-key");
|
|
70
|
+
const connection = await options.runtime.resolve();
|
|
71
|
+
if (!connection.transport.apiKey ||
|
|
72
|
+
connection.transport.authToken ||
|
|
73
|
+
sameSecret(connection.transport.apiKey, options.bridgeKey) ||
|
|
74
|
+
!sameSecret(supplied, connection.transport.apiKey))
|
|
75
|
+
throw new Refusal(403);
|
|
76
|
+
const base = new URL(connection.transport.remoteUrl);
|
|
77
|
+
const response = await network(new URL(`/api/v1/agent/workspaces/${workspaceId}/turns/${turnId}`, base), {
|
|
78
|
+
headers: buildUpstreamHeaders(connection.transport),
|
|
79
|
+
redirect: "error",
|
|
80
|
+
signal: AbortSignal.timeout(10_000),
|
|
81
|
+
});
|
|
82
|
+
if (!response.ok)
|
|
83
|
+
throw new Refusal(response.status >= 500 ? 503 : 403);
|
|
84
|
+
const turn = (await response.json());
|
|
85
|
+
if (turn.turnId !== turnId ||
|
|
86
|
+
turn.context?.workspaceId !== workspaceId ||
|
|
87
|
+
typeof turn.context.targetId !== "string" ||
|
|
88
|
+
typeof turn.context.tenantId !== "string" ||
|
|
89
|
+
!turn.context.tenantId)
|
|
90
|
+
throw new Refusal(403);
|
|
91
|
+
return { workspaceId, turnId, targetId: identifier(turn.context.targetId), partition, connection };
|
|
92
|
+
}
|
|
93
|
+
const boundFetch = async (input, init) => {
|
|
94
|
+
const binding = bindings.getStore();
|
|
95
|
+
if (!binding)
|
|
96
|
+
throw new Error("A verified native turn is required for upstream requests");
|
|
97
|
+
const url = new URL(input instanceof Request ? input.url : String(input));
|
|
98
|
+
if (url.origin !== new URL(binding.connection.transport.remoteUrl).origin)
|
|
99
|
+
throw new Error("Native upstream origin mismatch");
|
|
100
|
+
const headers = new Headers(input instanceof Request ? input.headers : undefined);
|
|
101
|
+
new Headers(init?.headers).forEach((value, key) => headers.set(key, value));
|
|
102
|
+
headers.set("X-Honua-Workspace", binding.workspaceId);
|
|
103
|
+
headers.set("X-Honua-Turn", binding.turnId);
|
|
104
|
+
return network(input, { ...init, headers, redirect: "error" });
|
|
105
|
+
};
|
|
106
|
+
function boundRuntime(initial) {
|
|
107
|
+
return {
|
|
108
|
+
inspect: async () => ({ ready: true, targetId: initial.targetId }),
|
|
109
|
+
install: async () => {
|
|
110
|
+
throw new Error("Native chat cannot bootstrap the host");
|
|
111
|
+
},
|
|
112
|
+
resolve: async () => {
|
|
113
|
+
const binding = bindings.getStore();
|
|
114
|
+
if (!binding || !samePrincipal(initial, binding))
|
|
115
|
+
throw new Error("Native gateway identity changed");
|
|
116
|
+
const connection = await options.runtime.resolve();
|
|
117
|
+
if (!samePrincipal(initial, { ...binding, connection }))
|
|
118
|
+
throw new Error("Native gateway credential changed");
|
|
119
|
+
return connection;
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
async function privateExtension(runtime) {
|
|
124
|
+
return options.devopsConfiguration ? configuredDevOps(options.devopsConfiguration, runtime, boundFetch) : undefined;
|
|
125
|
+
}
|
|
126
|
+
async function body(request) {
|
|
127
|
+
if (!String(request.headers["content-type"] ?? "")
|
|
128
|
+
.toLowerCase()
|
|
129
|
+
.startsWith("application/json"))
|
|
130
|
+
throw new Refusal(415);
|
|
131
|
+
const chunks = [];
|
|
132
|
+
let length = 0;
|
|
133
|
+
for await (const chunk of request) {
|
|
134
|
+
const bytes = Buffer.from(chunk);
|
|
135
|
+
length += bytes.length;
|
|
136
|
+
if (length > 256 * 1024)
|
|
137
|
+
throw new Refusal(413);
|
|
138
|
+
chunks.push(bytes);
|
|
139
|
+
}
|
|
140
|
+
try {
|
|
141
|
+
return JSON.parse(Buffer.concat(chunks).toString("utf8"));
|
|
142
|
+
}
|
|
143
|
+
catch {
|
|
144
|
+
throw new Refusal(400);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
async function handle(request, response) {
|
|
148
|
+
response.setHeader("Cache-Control", "no-store");
|
|
149
|
+
if (request.url !== "/mcp" && request.url !== "/capabilities")
|
|
150
|
+
throw new Refusal(404);
|
|
151
|
+
if (!["GET", "POST", "DELETE"].includes(request.method ?? ""))
|
|
152
|
+
throw new Refusal(405);
|
|
153
|
+
const binding = await authorize(request);
|
|
154
|
+
await bindings.run(binding, async () => {
|
|
155
|
+
if (request.url === "/capabilities") {
|
|
156
|
+
if (request.method !== "GET")
|
|
157
|
+
throw new Refusal(405);
|
|
158
|
+
const extension = await privateExtension(boundRuntime(binding));
|
|
159
|
+
let available = false;
|
|
160
|
+
try {
|
|
161
|
+
available = Boolean(extension && (await extension.listTools()).length);
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
/* Private authorization failure never widens the native catalog. */
|
|
165
|
+
}
|
|
166
|
+
finally {
|
|
167
|
+
await extension?.close();
|
|
168
|
+
}
|
|
169
|
+
response.setHeader("Content-Type", "application/json");
|
|
170
|
+
response.end(JSON.stringify({
|
|
171
|
+
schemaVersion: "honua.agent-gateway.capabilities.v1",
|
|
172
|
+
targetId: binding.targetId,
|
|
173
|
+
workspaceId: binding.workspaceId,
|
|
174
|
+
turnId: binding.turnId,
|
|
175
|
+
toolFamilies: available ? [DEVOPS_TOOL_FAMILY] : [],
|
|
176
|
+
}));
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
const sessionId = header(request, "mcp-session-id", true);
|
|
180
|
+
let session = sessionId ? sessions.get(sessionId) : undefined;
|
|
181
|
+
if (sessionId && (!session || !samePrincipal(session.binding, binding)))
|
|
182
|
+
throw new Refusal(403);
|
|
183
|
+
const parsed = request.method === "POST" ? await body(request) : undefined;
|
|
184
|
+
if (!session) {
|
|
185
|
+
const initialization = InitializeRequestSchema.safeParse(parsed);
|
|
186
|
+
if (sessionId || request.method !== "POST" || !initialization.success)
|
|
187
|
+
throw new Refusal(400);
|
|
188
|
+
if (sessions.size + initializing >= maximum)
|
|
189
|
+
throw new Refusal(429);
|
|
190
|
+
initializing++;
|
|
191
|
+
let gateway;
|
|
192
|
+
try {
|
|
193
|
+
const runtime = boundRuntime(binding);
|
|
194
|
+
gateway = new HonuaAgentGateway(runtime, (connection, view) => connectAgentUpstream(connection, view, boundFetch), await privateExtension(runtime), { bootstrapTools: false });
|
|
195
|
+
// Attach before initialize's response so immediate discovery is authoritative.
|
|
196
|
+
await gateway.prepare(readWorkflowView(initialization.data));
|
|
197
|
+
const id = randomUUID();
|
|
198
|
+
const transport = new StreamableHTTPServerTransport({
|
|
199
|
+
sessionIdGenerator: () => id,
|
|
200
|
+
enableJsonResponse: true,
|
|
201
|
+
});
|
|
202
|
+
await gateway.connect(transport);
|
|
203
|
+
session = { binding, gateway, transport, touched: Date.now(), active: 0 };
|
|
204
|
+
sessions.set(id, session);
|
|
205
|
+
const previous = transport.onclose;
|
|
206
|
+
transport.onclose = () => {
|
|
207
|
+
sessions.delete(id);
|
|
208
|
+
previous?.();
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
catch (error) {
|
|
212
|
+
await gateway?.close().catch(() => { });
|
|
213
|
+
throw error;
|
|
214
|
+
}
|
|
215
|
+
finally {
|
|
216
|
+
initializing--;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
session.touched = Date.now();
|
|
220
|
+
session.active++;
|
|
221
|
+
try {
|
|
222
|
+
await session.transport.handleRequest(request, response, parsed);
|
|
223
|
+
}
|
|
224
|
+
finally {
|
|
225
|
+
session.active--;
|
|
226
|
+
session.touched = Date.now();
|
|
227
|
+
}
|
|
228
|
+
});
|
|
229
|
+
}
|
|
230
|
+
const server = createServer((request, response) => {
|
|
231
|
+
void handle(request, response).catch((error) => {
|
|
232
|
+
if (!response.headersSent) {
|
|
233
|
+
response.writeHead(error instanceof Refusal ? error.status : 503, {
|
|
234
|
+
"Content-Type": "application/json",
|
|
235
|
+
"Cache-Control": "no-store",
|
|
236
|
+
});
|
|
237
|
+
response.end('{"error":"Native gateway request unavailable or unauthorized"}');
|
|
238
|
+
}
|
|
239
|
+
else
|
|
240
|
+
response.end();
|
|
241
|
+
});
|
|
242
|
+
});
|
|
243
|
+
server.headersTimeout = 10_000;
|
|
244
|
+
server.requestTimeout = 30_000;
|
|
245
|
+
const sweep = setInterval(() => {
|
|
246
|
+
for (const [id, session] of sessions) {
|
|
247
|
+
if (session.active === 0 && Date.now() - session.touched > idle) {
|
|
248
|
+
sessions.delete(id);
|
|
249
|
+
void session.gateway.close().catch(() => { });
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
}, Math.min(idle, 30_000));
|
|
253
|
+
sweep.unref();
|
|
254
|
+
server.once("close", () => {
|
|
255
|
+
clearInterval(sweep);
|
|
256
|
+
for (const session of sessions.values())
|
|
257
|
+
void session.gateway.close().catch(() => { });
|
|
258
|
+
sessions.clear();
|
|
259
|
+
});
|
|
260
|
+
return server;
|
|
261
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/** Versioned semantic guidance, not an executor or a claim of live qualification. */
|
|
2
|
+
export declare const WORKFLOW_KNOWLEDGE_URI = "honua://agent/knowledge/workflows/v1";
|
|
3
|
+
export declare const WORKFLOW_KNOWLEDGE: {
|
|
4
|
+
readonly schemaVersion: "honua.agent-workflow-knowledge/v1";
|
|
5
|
+
readonly knowledgeVersion: "2026-10-17.2";
|
|
6
|
+
readonly qualification: "contract-guidance-only";
|
|
7
|
+
readonly authority: readonly ["Use discovered canonical tool schemas; these workflow descriptions never grant authorization.", "Bind every step to the current principal, tenant and target. Recheck credentials and RBAC on each action.", "Persist resource, operation and command references from canonical responses. Model prose cannot create a verified observation.", "After a lost mutation response reconcile the durable command/operation before retry; never infer failure from a timeout.", "The human approval inbox and approving principal are distinct from the agent. AwaitingApproval is a successful handoff, not a published result.", "Keep bootstrap secrets, cloud credentials, signing keys and private environment values outside model context."];
|
|
8
|
+
readonly workflows: readonly [{
|
|
9
|
+
readonly id: "local-bootstrap";
|
|
10
|
+
readonly intent: "Evaluate Honua locally from one persistent MCP registration.";
|
|
11
|
+
readonly preconditions: readonly ["Host-selected installation directory", "Local Docker and compatible Compose", "Explicit authorization to install"];
|
|
12
|
+
readonly actions: readonly ["honua_agent_inspect_local", "honua_agent_install_local", "honua_agent_connect", "honua_agent_status"];
|
|
13
|
+
readonly inputReferences: readonly ["host installation profile"];
|
|
14
|
+
readonly outputReferences: readonly ["verified target id", "credential id without credential value"];
|
|
15
|
+
readonly observedOutcomes: readonly ["Docker readiness", "authenticated server MCP negotiation", "separate human pairing where required"];
|
|
16
|
+
readonly failureRule: "Installation output alone is not a verified or human-paired connection.";
|
|
17
|
+
}, {
|
|
18
|
+
readonly id: "shared-workspace-context";
|
|
19
|
+
readonly intent: "Continue the same authorized GIS task through chat, Console forms and Studio.";
|
|
20
|
+
readonly preconditions: readonly ["Discovered honua_workspace_* tools", "Actual paired workspace and explicit browser view references", "Current workspace membership, target and resource authorization"];
|
|
21
|
+
readonly actions: readonly ["honua_workspace_get", "honua_workspace_set_context when changing selection or task; retain expectedRevision", "honua_workspace_create_turn before execution; capture the submitted contextRevision", "honua_workspace_get_turn for the active prompt instead of rereading a later selection", "honua_workspace_get_draft and honua_workspace_patch_draft for an existing publish-layer form", "honua_workspace_submit_draft with a persistent commandId", "honua_workspace_get_command before retrying a submission with an unknown outcome", "honua_workspace_append_message for conversation text, never for verified operation receipts"];
|
|
22
|
+
readonly inputReferences: readonly ["workspace id", "view id", "target id", "context revision", "selected service/layer references", "task reference from an actual canonical response"];
|
|
23
|
+
readonly outputReferences: readonly ["immutable turn id", "shared draft id and revision", "command id", "canonical operation/proposal id"];
|
|
24
|
+
readonly observedOutcomes: readonly ["current authorized selection", "frozen prompt target", "shared form revision", "canonical command state"];
|
|
25
|
+
readonly failureRule: "Do not invent browser/workspace references, overwrite dirty forms or retarget an existing turn after navigation. A Studio task binds its real draft id/generation/package key; a workflow-package task binds its immutable package id/version/content hash. A local custom-code run is not a server workflow-package task. Native chat supplies request bindings through its backend; external clients must use the discovered explicit workspace tool contracts and must not assume automatic header binding.";
|
|
26
|
+
}, {
|
|
27
|
+
readonly id: "data-and-services";
|
|
28
|
+
readonly intent: "Connect or import data, publish a service and verify a query.";
|
|
29
|
+
readonly preconditions: readonly ["Verified current target", "Caller import/connect and publish permissions", "Explicit source and destination"];
|
|
30
|
+
readonly actions: readonly ["Discover connection/import tools", "Validate source schema and geometry/CRS", "Execute canonical import or connection", "Publish via discovered service/layer tool", "Query the published layer"];
|
|
31
|
+
readonly inputReferences: readonly ["connection/source reference", "upload/artifact reference", "destination schema"];
|
|
32
|
+
readonly outputReferences: readonly ["import job id", "service id", "layer id or protocol-neutral source reference"];
|
|
33
|
+
readonly observedOutcomes: readonly ["terminal import outcome", "service catalog entry", "query result with expected schema and representative data"];
|
|
34
|
+
readonly failureRule: "An accepted job or publish request is not successful ingestion or query verification.";
|
|
35
|
+
}, {
|
|
36
|
+
readonly id: "studio-map-and-publication";
|
|
37
|
+
readonly intent: "Compose/style a map or app, save/reopen its version and publish it for an audience.";
|
|
38
|
+
readonly preconditions: readonly ["Actual service/layer references", "Shared target and selection", "Current draft revision", "Caller draft/publish permissions"];
|
|
39
|
+
readonly actions: readonly ["Discover Studio tool schemas", "Create/edit draft under current policy", "Validate", "Save immutable version", "Read/reopen saved version", "Publish using canonical publication tool", "Verify published audience access"];
|
|
40
|
+
readonly inputReferences: readonly ["draft id and revision", "service/layer/style references", "intended audience"];
|
|
41
|
+
readonly outputReferences: readonly ["draft id", "version id", "publication operation/proposal id", "published app/map reference"];
|
|
42
|
+
readonly observedOutcomes: readonly ["saved content reread", "unrelated edits preserved", "Published or AwaitingApproval receipt", "audience access checked after Published"];
|
|
43
|
+
readonly failureRule: "On conflict reread/reconcile against current revision. On AwaitingApproval preserve proposal id and hand off to the human inbox; do not claim publication.";
|
|
44
|
+
}, {
|
|
45
|
+
readonly id: "processes";
|
|
46
|
+
readonly intent: "Run a built-in process graph or develop separately sandboxed Python/.NET code and map its output.";
|
|
47
|
+
readonly preconditions: readonly ["Discovered Processes capabilities", "Input resource references", "Explicit built-in versus custom-code execution mode"];
|
|
48
|
+
readonly actions: readonly ["Snapshot inputs and graph/code immutably", "Validate/build in the selected adapter", "Run with bounded resources", "Read sanitized diagnostics", "Correct and rerun as a new snapshot when needed", "Import output with normal artifact/import tools", "Query/map the imported output"];
|
|
49
|
+
readonly inputReferences: readonly ["graph definition or isolated code snapshot", "input artifact/layer ids", "adapter/runtime version"];
|
|
50
|
+
readonly outputReferences: readonly ["snapshot/build/run ids", "diagnostic references", "output artifact id", "imported layer reference"];
|
|
51
|
+
readonly observedOutcomes: readonly ["failed and corrected run statuses", "cancellation status when requested", "output schema/CRS", "query/map evidence"];
|
|
52
|
+
readonly failureRule: "Do not mix built-in/custom-code graphs or claim syntax-valid code executed. A run artifact is not an imported layer.";
|
|
53
|
+
}, {
|
|
54
|
+
readonly id: "cloud-git-and-recovery";
|
|
55
|
+
readonly intent: "Optionally provision ECS, version scoped GIS configuration, and operate Honua through canonical governed actions.";
|
|
56
|
+
readonly preconditions: readonly ["Optional private DevOps configured by host", "Per-action credential/RBAC check", "Explicit ECS target/environment", "Human-reviewed plan and configured approval"];
|
|
57
|
+
readonly actions: readonly ["Describe environment", "Plan aws-ecs via private canonical executor", "Bind separate signed approval to exact saved-plan digest", "Apply exact approved plan", "Emit and verify secretless handoff", "Generate scoped metadata Git change set", "Create scoped review commit/PR through create_scoped_gitops_pull_request with the exact configured base", "Propose config recovery through revert_scoped_gitops_pull_request; separate humans review/merge/approve", "Request version update or recovery through server-owned ops/proposals"];
|
|
58
|
+
readonly inputReferences: readonly ["ECS provisioning operation and plan digest", "separate approval receipt", "server metadata release package", "current and candidate revision", "known-good revision/recovery policy"];
|
|
59
|
+
readonly outputReferences: readonly ["provision binding", "verified handoff receipt", "Git commit/PR ids", "canonical proposal/deploy operation ids", "recovery evidence"];
|
|
60
|
+
readonly observedOutcomes: readonly ["exact-plan executor outcome", "authenticated handoff", "scoped Git diff and commit reread", "terminal server deployment outcome", "prior revision restored and rejected candidate quarantined only when qualified recovery is proven"];
|
|
61
|
+
readonly failureRule: "ECS provisioning does not qualify ECS version rollback/recovery. Lambda/EKS require real adapters and separate proof. Metadata change-set generation does not write Git; optional scoped PR/revert adapters write only deterministic review branches and never merge or submit deployment. Syntax/contract tests do not establish live cloud qualification.";
|
|
62
|
+
}, {
|
|
63
|
+
readonly id: "day2-operations";
|
|
64
|
+
readonly intent: "Inspect health and propose version/recovery operations without a parallel execution or approval loop.";
|
|
65
|
+
readonly preconditions: readonly ["Authenticated server target", "Discovered honua_ops_* and honua_propose_* capabilities", "Caller operation permissions"];
|
|
66
|
+
readonly actions: readonly ["Read canonical health/findings", "Inspect supported operation schema and protection capability", "Create scoped server proposal", "Hand proposal to separate human approval inbox", "Observe canonical operation and verify actual service state"];
|
|
67
|
+
readonly inputReferences: readonly ["target id", "findings/evidence refs", "current/candidate/prior revisions where applicable"];
|
|
68
|
+
readonly outputReferences: readonly ["proposal id", "operation id", "approval/audit reference", "observed service revision"];
|
|
69
|
+
readonly observedOutcomes: readonly ["AwaitingApproval or canonical execution outcome", "version/health reread", "recovery qualification bound to exact backend and server artifact"];
|
|
70
|
+
readonly failureRule: "Agent cannot approve its own proposal. Missing adapter/protection evidence is unsupported or unqualified; do not synthesize a rollback or call private experimental rollback.";
|
|
71
|
+
}];
|
|
72
|
+
};
|
|
73
|
+
//# sourceMappingURL=workflow-knowledge.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflow-knowledge.d.ts","sourceRoot":"","sources":["../../../src/agent/workflow-knowledge.ts"],"names":[],"mappings":"AAAA,qFAAqF;AACrF,eAAO,MAAM,sBAAsB,yCAAyC,CAAC;AAC7E,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAyOrB,CAAC"}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
/** Versioned semantic guidance, not an executor or a claim of live qualification. */
|
|
2
|
+
export const WORKFLOW_KNOWLEDGE_URI = "honua://agent/knowledge/workflows/v1";
|
|
3
|
+
export const WORKFLOW_KNOWLEDGE = {
|
|
4
|
+
schemaVersion: "honua.agent-workflow-knowledge/v1",
|
|
5
|
+
knowledgeVersion: "2026-10-17.2",
|
|
6
|
+
qualification: "contract-guidance-only",
|
|
7
|
+
authority: [
|
|
8
|
+
"Use discovered canonical tool schemas; these workflow descriptions never grant authorization.",
|
|
9
|
+
"Bind every step to the current principal, tenant and target. Recheck credentials and RBAC on each action.",
|
|
10
|
+
"Persist resource, operation and command references from canonical responses. Model prose cannot create a verified observation.",
|
|
11
|
+
"After a lost mutation response reconcile the durable command/operation before retry; never infer failure from a timeout.",
|
|
12
|
+
"The human approval inbox and approving principal are distinct from the agent. AwaitingApproval is a successful handoff, not a published result.",
|
|
13
|
+
"Keep bootstrap secrets, cloud credentials, signing keys and private environment values outside model context.",
|
|
14
|
+
],
|
|
15
|
+
workflows: [
|
|
16
|
+
{
|
|
17
|
+
id: "local-bootstrap",
|
|
18
|
+
intent: "Evaluate Honua locally from one persistent MCP registration.",
|
|
19
|
+
preconditions: [
|
|
20
|
+
"Host-selected installation directory",
|
|
21
|
+
"Local Docker and compatible Compose",
|
|
22
|
+
"Explicit authorization to install",
|
|
23
|
+
],
|
|
24
|
+
actions: ["honua_agent_inspect_local", "honua_agent_install_local", "honua_agent_connect", "honua_agent_status"],
|
|
25
|
+
inputReferences: ["host installation profile"],
|
|
26
|
+
outputReferences: ["verified target id", "credential id without credential value"],
|
|
27
|
+
observedOutcomes: [
|
|
28
|
+
"Docker readiness",
|
|
29
|
+
"authenticated server MCP negotiation",
|
|
30
|
+
"separate human pairing where required",
|
|
31
|
+
],
|
|
32
|
+
failureRule: "Installation output alone is not a verified or human-paired connection.",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: "shared-workspace-context",
|
|
36
|
+
intent: "Continue the same authorized GIS task through chat, Console forms and Studio.",
|
|
37
|
+
preconditions: [
|
|
38
|
+
"Discovered honua_workspace_* tools",
|
|
39
|
+
"Actual paired workspace and explicit browser view references",
|
|
40
|
+
"Current workspace membership, target and resource authorization",
|
|
41
|
+
],
|
|
42
|
+
actions: [
|
|
43
|
+
"honua_workspace_get",
|
|
44
|
+
"honua_workspace_set_context when changing selection or task; retain expectedRevision",
|
|
45
|
+
"honua_workspace_create_turn before execution; capture the submitted contextRevision",
|
|
46
|
+
"honua_workspace_get_turn for the active prompt instead of rereading a later selection",
|
|
47
|
+
"honua_workspace_get_draft and honua_workspace_patch_draft for an existing publish-layer form",
|
|
48
|
+
"honua_workspace_submit_draft with a persistent commandId",
|
|
49
|
+
"honua_workspace_get_command before retrying a submission with an unknown outcome",
|
|
50
|
+
"honua_workspace_append_message for conversation text, never for verified operation receipts",
|
|
51
|
+
],
|
|
52
|
+
inputReferences: [
|
|
53
|
+
"workspace id",
|
|
54
|
+
"view id",
|
|
55
|
+
"target id",
|
|
56
|
+
"context revision",
|
|
57
|
+
"selected service/layer references",
|
|
58
|
+
"task reference from an actual canonical response",
|
|
59
|
+
],
|
|
60
|
+
outputReferences: [
|
|
61
|
+
"immutable turn id",
|
|
62
|
+
"shared draft id and revision",
|
|
63
|
+
"command id",
|
|
64
|
+
"canonical operation/proposal id",
|
|
65
|
+
],
|
|
66
|
+
observedOutcomes: [
|
|
67
|
+
"current authorized selection",
|
|
68
|
+
"frozen prompt target",
|
|
69
|
+
"shared form revision",
|
|
70
|
+
"canonical command state",
|
|
71
|
+
],
|
|
72
|
+
failureRule: "Do not invent browser/workspace references, overwrite dirty forms or retarget an existing turn after navigation. A Studio task binds its real draft id/generation/package key; a workflow-package task binds its immutable package id/version/content hash. A local custom-code run is not a server workflow-package task. Native chat supplies request bindings through its backend; external clients must use the discovered explicit workspace tool contracts and must not assume automatic header binding.",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: "data-and-services",
|
|
76
|
+
intent: "Connect or import data, publish a service and verify a query.",
|
|
77
|
+
preconditions: [
|
|
78
|
+
"Verified current target",
|
|
79
|
+
"Caller import/connect and publish permissions",
|
|
80
|
+
"Explicit source and destination",
|
|
81
|
+
],
|
|
82
|
+
actions: [
|
|
83
|
+
"Discover connection/import tools",
|
|
84
|
+
"Validate source schema and geometry/CRS",
|
|
85
|
+
"Execute canonical import or connection",
|
|
86
|
+
"Publish via discovered service/layer tool",
|
|
87
|
+
"Query the published layer",
|
|
88
|
+
],
|
|
89
|
+
inputReferences: ["connection/source reference", "upload/artifact reference", "destination schema"],
|
|
90
|
+
outputReferences: ["import job id", "service id", "layer id or protocol-neutral source reference"],
|
|
91
|
+
observedOutcomes: [
|
|
92
|
+
"terminal import outcome",
|
|
93
|
+
"service catalog entry",
|
|
94
|
+
"query result with expected schema and representative data",
|
|
95
|
+
],
|
|
96
|
+
failureRule: "An accepted job or publish request is not successful ingestion or query verification.",
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
id: "studio-map-and-publication",
|
|
100
|
+
intent: "Compose/style a map or app, save/reopen its version and publish it for an audience.",
|
|
101
|
+
preconditions: [
|
|
102
|
+
"Actual service/layer references",
|
|
103
|
+
"Shared target and selection",
|
|
104
|
+
"Current draft revision",
|
|
105
|
+
"Caller draft/publish permissions",
|
|
106
|
+
],
|
|
107
|
+
actions: [
|
|
108
|
+
"Discover Studio tool schemas",
|
|
109
|
+
"Create/edit draft under current policy",
|
|
110
|
+
"Validate",
|
|
111
|
+
"Save immutable version",
|
|
112
|
+
"Read/reopen saved version",
|
|
113
|
+
"Publish using canonical publication tool",
|
|
114
|
+
"Verify published audience access",
|
|
115
|
+
],
|
|
116
|
+
inputReferences: ["draft id and revision", "service/layer/style references", "intended audience"],
|
|
117
|
+
outputReferences: ["draft id", "version id", "publication operation/proposal id", "published app/map reference"],
|
|
118
|
+
observedOutcomes: [
|
|
119
|
+
"saved content reread",
|
|
120
|
+
"unrelated edits preserved",
|
|
121
|
+
"Published or AwaitingApproval receipt",
|
|
122
|
+
"audience access checked after Published",
|
|
123
|
+
],
|
|
124
|
+
failureRule: "On conflict reread/reconcile against current revision. On AwaitingApproval preserve proposal id and hand off to the human inbox; do not claim publication.",
|
|
125
|
+
},
|
|
126
|
+
{
|
|
127
|
+
id: "processes",
|
|
128
|
+
intent: "Run a built-in process graph or develop separately sandboxed Python/.NET code and map its output.",
|
|
129
|
+
preconditions: [
|
|
130
|
+
"Discovered Processes capabilities",
|
|
131
|
+
"Input resource references",
|
|
132
|
+
"Explicit built-in versus custom-code execution mode",
|
|
133
|
+
],
|
|
134
|
+
actions: [
|
|
135
|
+
"Snapshot inputs and graph/code immutably",
|
|
136
|
+
"Validate/build in the selected adapter",
|
|
137
|
+
"Run with bounded resources",
|
|
138
|
+
"Read sanitized diagnostics",
|
|
139
|
+
"Correct and rerun as a new snapshot when needed",
|
|
140
|
+
"Import output with normal artifact/import tools",
|
|
141
|
+
"Query/map the imported output",
|
|
142
|
+
],
|
|
143
|
+
inputReferences: [
|
|
144
|
+
"graph definition or isolated code snapshot",
|
|
145
|
+
"input artifact/layer ids",
|
|
146
|
+
"adapter/runtime version",
|
|
147
|
+
],
|
|
148
|
+
outputReferences: [
|
|
149
|
+
"snapshot/build/run ids",
|
|
150
|
+
"diagnostic references",
|
|
151
|
+
"output artifact id",
|
|
152
|
+
"imported layer reference",
|
|
153
|
+
],
|
|
154
|
+
observedOutcomes: [
|
|
155
|
+
"failed and corrected run statuses",
|
|
156
|
+
"cancellation status when requested",
|
|
157
|
+
"output schema/CRS",
|
|
158
|
+
"query/map evidence",
|
|
159
|
+
],
|
|
160
|
+
failureRule: "Do not mix built-in/custom-code graphs or claim syntax-valid code executed. A run artifact is not an imported layer.",
|
|
161
|
+
},
|
|
162
|
+
{
|
|
163
|
+
id: "cloud-git-and-recovery",
|
|
164
|
+
intent: "Optionally provision ECS, version scoped GIS configuration, and operate Honua through canonical governed actions.",
|
|
165
|
+
preconditions: [
|
|
166
|
+
"Optional private DevOps configured by host",
|
|
167
|
+
"Per-action credential/RBAC check",
|
|
168
|
+
"Explicit ECS target/environment",
|
|
169
|
+
"Human-reviewed plan and configured approval",
|
|
170
|
+
],
|
|
171
|
+
actions: [
|
|
172
|
+
"Describe environment",
|
|
173
|
+
"Plan aws-ecs via private canonical executor",
|
|
174
|
+
"Bind separate signed approval to exact saved-plan digest",
|
|
175
|
+
"Apply exact approved plan",
|
|
176
|
+
"Emit and verify secretless handoff",
|
|
177
|
+
"Generate scoped metadata Git change set",
|
|
178
|
+
"Create scoped review commit/PR through create_scoped_gitops_pull_request with the exact configured base",
|
|
179
|
+
"Propose config recovery through revert_scoped_gitops_pull_request; separate humans review/merge/approve",
|
|
180
|
+
"Request version update or recovery through server-owned ops/proposals",
|
|
181
|
+
],
|
|
182
|
+
inputReferences: [
|
|
183
|
+
"ECS provisioning operation and plan digest",
|
|
184
|
+
"separate approval receipt",
|
|
185
|
+
"server metadata release package",
|
|
186
|
+
"current and candidate revision",
|
|
187
|
+
"known-good revision/recovery policy",
|
|
188
|
+
],
|
|
189
|
+
outputReferences: [
|
|
190
|
+
"provision binding",
|
|
191
|
+
"verified handoff receipt",
|
|
192
|
+
"Git commit/PR ids",
|
|
193
|
+
"canonical proposal/deploy operation ids",
|
|
194
|
+
"recovery evidence",
|
|
195
|
+
],
|
|
196
|
+
observedOutcomes: [
|
|
197
|
+
"exact-plan executor outcome",
|
|
198
|
+
"authenticated handoff",
|
|
199
|
+
"scoped Git diff and commit reread",
|
|
200
|
+
"terminal server deployment outcome",
|
|
201
|
+
"prior revision restored and rejected candidate quarantined only when qualified recovery is proven",
|
|
202
|
+
],
|
|
203
|
+
failureRule: "ECS provisioning does not qualify ECS version rollback/recovery. Lambda/EKS require real adapters and separate proof. Metadata change-set generation does not write Git; optional scoped PR/revert adapters write only deterministic review branches and never merge or submit deployment. Syntax/contract tests do not establish live cloud qualification.",
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
id: "day2-operations",
|
|
207
|
+
intent: "Inspect health and propose version/recovery operations without a parallel execution or approval loop.",
|
|
208
|
+
preconditions: [
|
|
209
|
+
"Authenticated server target",
|
|
210
|
+
"Discovered honua_ops_* and honua_propose_* capabilities",
|
|
211
|
+
"Caller operation permissions",
|
|
212
|
+
],
|
|
213
|
+
actions: [
|
|
214
|
+
"Read canonical health/findings",
|
|
215
|
+
"Inspect supported operation schema and protection capability",
|
|
216
|
+
"Create scoped server proposal",
|
|
217
|
+
"Hand proposal to separate human approval inbox",
|
|
218
|
+
"Observe canonical operation and verify actual service state",
|
|
219
|
+
],
|
|
220
|
+
inputReferences: ["target id", "findings/evidence refs", "current/candidate/prior revisions where applicable"],
|
|
221
|
+
outputReferences: ["proposal id", "operation id", "approval/audit reference", "observed service revision"],
|
|
222
|
+
observedOutcomes: [
|
|
223
|
+
"AwaitingApproval or canonical execution outcome",
|
|
224
|
+
"version/health reread",
|
|
225
|
+
"recovery qualification bound to exact backend and server artifact",
|
|
226
|
+
],
|
|
227
|
+
failureRule: "Agent cannot approve its own proposal. Missing adapter/protection evidence is unsupported or unqualified; do not synthesize a rollback or call private experimental rollback.",
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
};
|