@tomflow/proflow-agent-gateway 0.1.16 → 0.1.18

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # @tomflow/proflow-agent-gateway
2
2
 
3
+ ## 0.1.18
4
+
5
+ ### Patch Changes
6
+
7
+ - Aggregate Browser, Permission, Host-application, Provisioning, Direct Tool, and Gateway observability at stable runtime boundaries, preserving owner truth while enabling one operation-chain diagnosis path without logging sensitive payloads.
8
+
9
+ ## 0.1.17
10
+
11
+ ### Patch Changes
12
+
13
+ - Close the Real-3 runtime reconciliation, health, browser effect-result boundary, observer pagination, and standalone execution-runtime configuration gaps for final human acceptance.
14
+
3
15
  ## 0.1.16
4
16
 
5
17
  ### Patch Changes
@@ -6,7 +6,7 @@ export declare const behaviorAdapter: {
6
6
  ok: true;
7
7
  status: "SUCCEEDED";
8
8
  moduleRef: "agent-gateway";
9
- moduleVersion: "0.1.16";
9
+ moduleVersion: "0.1.18";
10
10
  data: {
11
11
  localBaseUrl: string;
12
12
  publicBaseUrl?: string;
@@ -20,7 +20,7 @@ export declare const behaviorAdapter: {
20
20
  readonly ok: true;
21
21
  readonly status: "SUCCEEDED";
22
22
  readonly moduleRef: "agent-gateway";
23
- readonly moduleVersion: "0.1.16";
23
+ readonly moduleVersion: "0.1.18";
24
24
  };
25
25
  observedEffects: string[];
26
26
  }>;
@@ -30,7 +30,7 @@ export declare const behaviorAdapter: {
30
30
  ok: true;
31
31
  status: "SUCCEEDED";
32
32
  moduleRef: "agent-gateway";
33
- moduleVersion: "0.1.16";
33
+ moduleVersion: "0.1.18";
34
34
  data: {
35
35
  setupStatus: "BLOCKED" | "READY";
36
36
  runtimeStatus: "RUNNING" | "STOPPED";
@@ -51,7 +51,7 @@ export declare const behaviorAdapter: {
51
51
  ok: true;
52
52
  status: "SUCCEEDED";
53
53
  moduleRef: "agent-gateway";
54
- moduleVersion: "0.1.16";
54
+ moduleVersion: "0.1.18";
55
55
  data: {
56
56
  localBaseUrl: string;
57
57
  publicBaseUrl?: string;
@@ -66,7 +66,7 @@ export declare const behaviorAdapter: {
66
66
  ok: true;
67
67
  status: "SUCCEEDED";
68
68
  moduleRef: "agent-gateway";
69
- moduleVersion: "0.1.16";
69
+ moduleVersion: "0.1.18";
70
70
  data: {
71
71
  docs: string;
72
72
  };
@@ -79,7 +79,7 @@ export declare const behaviorAdapter: {
79
79
  ok: true;
80
80
  status: "SUCCEEDED";
81
81
  moduleRef: "agent-gateway";
82
- moduleVersion: "0.1.16";
82
+ moduleVersion: "0.1.18";
83
83
  data: {
84
84
  host: string;
85
85
  port: number;
@@ -90,7 +90,7 @@ export declare const behaviorAdapter: {
90
90
  result: {
91
91
  contract: "deployment.result.v1";
92
92
  moduleRef: "agent-gateway";
93
- moduleVersion: "0.1.16";
93
+ moduleVersion: "0.1.18";
94
94
  ok: false;
95
95
  status: "FAILED";
96
96
  error: {
@@ -107,7 +107,7 @@ export declare const behaviorAdapter: {
107
107
  readonly ok: true;
108
108
  readonly status: "SUCCEEDED";
109
109
  readonly moduleRef: "agent-gateway";
110
- readonly moduleVersion: "0.1.16";
110
+ readonly moduleVersion: "0.1.18";
111
111
  };
112
112
  observedEffects: string[];
113
113
  } | {
@@ -116,11 +116,11 @@ export declare const behaviorAdapter: {
116
116
  readonly ok: true;
117
117
  readonly status: "SUCCEEDED";
118
118
  readonly moduleRef: "agent-gateway";
119
- readonly moduleVersion: "0.1.16";
119
+ readonly moduleVersion: "0.1.18";
120
120
  } | {
121
121
  contract: "deployment.result.v1";
122
122
  moduleRef: "agent-gateway";
123
- moduleVersion: "0.1.16";
123
+ moduleVersion: "0.1.18";
124
124
  ok: false;
125
125
  status: "FAILED";
126
126
  error: {
@@ -1,5 +1,6 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
- import { join, resolve } from "node:path";
2
+ import { appendFile, mkdir } from "node:fs/promises";
3
+ import { dirname, join, resolve } from "node:path";
3
4
  import { deterministicLoopbackPort, readModuleSharedFacts, writeModuleSharedFacts, } from "@tomflow/proflow-module-contract";
4
5
  import { descriptor } from "./descriptor.js";
5
6
  const services = new Map();
@@ -56,8 +57,8 @@ async function running(context) {
56
57
  if (!own)
57
58
  return false;
58
59
  try {
59
- return (await fetch(`${own.localBaseUrl}/ready`, {
60
- signal: AbortSignal.timeout(500),
60
+ return (await fetch(`${own.localBaseUrl}/health`, {
61
+ signal: AbortSignal.timeout(2_000),
61
62
  })).ok;
62
63
  }
63
64
  catch {
@@ -72,6 +73,20 @@ async function compose(context) {
72
73
  const credentialFile = join(deps.stateRoot, "agent", "secrets", "role-credentials.json");
73
74
  if (!existsSync(credentialFile))
74
75
  throw new Error("Agent role credential store is not materialized by Platform Host");
76
+ const logPath = join(deps.stateRoot, "logs", "agent-gateway", "events.jsonl");
77
+ let logTail = Promise.resolve();
78
+ const log = (entry) => {
79
+ logTail = logTail
80
+ .catch(() => undefined)
81
+ .then(async () => {
82
+ await mkdir(dirname(logPath), { recursive: true, mode: 0o700 });
83
+ await appendFile(logPath, `${JSON.stringify(entry)}\n`, {
84
+ encoding: "utf8",
85
+ mode: 0o600,
86
+ });
87
+ });
88
+ void logTail;
89
+ };
75
90
  const { createAgentGatewayProcess, parseAgentGatewayProcessConfig } = await import("../src/process.js");
76
91
  const listener = new URL(own.localBaseUrl);
77
92
  return createAgentGatewayProcess({
@@ -83,6 +98,8 @@ async function compose(context) {
83
98
  credentialFile,
84
99
  downstreamCredentialFile: deps.downstreamCredentialFile,
85
100
  }),
101
+ log,
102
+ operationLog: log,
86
103
  });
87
104
  }
88
105
  const failed = (code, message) => ({
@@ -104,7 +121,9 @@ export const behaviorAdapter = {
104
121
  }
105
122
  return {
106
123
  result: base,
107
- observedEffects: service ? ["Manage the declared service process"] : [],
124
+ observedEffects: service
125
+ ? ["Manage the declared service process"]
126
+ : [],
108
127
  };
109
128
  },
110
129
  status: async (context) => {
@@ -157,7 +176,9 @@ export const behaviorAdapter = {
157
176
  result: {
158
177
  ...base,
159
178
  data: {
160
- docs: readFileSync(new URL(import.meta.url.includes("/dist/") ? "../../DOCS.md" : "../DOCS.md", import.meta.url), "utf8"),
179
+ docs: readFileSync(new URL(import.meta.url.includes("/dist/")
180
+ ? "../../DOCS.md"
181
+ : "../DOCS.md", import.meta.url), "utf8"),
161
182
  },
162
183
  },
163
184
  observedEffects: [],
@@ -175,7 +196,9 @@ export const behaviorAdapter = {
175
196
  }
176
197
  catch (error) {
177
198
  return {
178
- result: failed("START_FAILED", error instanceof Error ? error.message : "agent-gateway start failed"),
199
+ result: failed("START_FAILED", error instanceof Error
200
+ ? error.message
201
+ : "agent-gateway start failed"),
179
202
  observedEffects: [],
180
203
  };
181
204
  }
@@ -3,7 +3,7 @@ export declare const descriptor: {
3
3
  readonly contractVersion: "1.0.0";
4
4
  readonly moduleRef: "agent-gateway";
5
5
  readonly packageName: "@tomflow/proflow-agent-gateway";
6
- readonly moduleVersion: "0.1.16";
6
+ readonly moduleVersion: "0.1.18";
7
7
  readonly kind: "service";
8
8
  readonly templateVersion: "1.0.0";
9
9
  readonly platformCompatibility: ">=1.0.0 <2.0.0";
@@ -21,9 +21,6 @@ export declare const descriptor: {
21
21
  }, {
22
22
  readonly contractRef: "task-orchestration";
23
23
  readonly versionRange: ">=1.0.0 <2.0.0";
24
- }, {
25
- readonly contractRef: "execution";
26
- readonly versionRange: ">=1.0.0 <2.0.0";
27
24
  }, {
28
25
  readonly contractRef: "platform-host";
29
26
  readonly versionRange: ">=1.0.0 <2.0.0";
@@ -3,7 +3,7 @@ export const descriptor = {
3
3
  contractVersion: "1.0.0",
4
4
  moduleRef: "agent-gateway",
5
5
  packageName: "@tomflow/proflow-agent-gateway",
6
- moduleVersion: "0.1.16",
6
+ moduleVersion: "0.1.18",
7
7
  kind: "service",
8
8
  templateVersion: "1.0.0",
9
9
  platformCompatibility: ">=1.0.0 <2.0.0",
@@ -26,10 +26,6 @@ export const descriptor = {
26
26
  contractRef: "task-orchestration",
27
27
  versionRange: ">=1.0.0 <2.0.0",
28
28
  },
29
- {
30
- contractRef: "execution",
31
- versionRange: ">=1.0.0 <2.0.0",
32
- },
33
29
  {
34
30
  contractRef: "platform-host",
35
31
  versionRange: ">=1.0.0 <2.0.0",
@@ -9,6 +9,7 @@ export type GatewayOwnerPorts = {
9
9
  authenticateBearer(credential: string): Promise<string>;
10
10
  route(operationId: string, authenticatedRoleRef: string, input: unknown, context?: {
11
11
  signal: AbortSignal;
12
+ deadlineAt: string;
12
13
  fileMaterializationInputs?: readonly ExternalFileMaterializationInput[];
13
14
  }): Promise<unknown>;
14
15
  lookupResult?(operationId: string, authenticatedRoleRef: string, input: unknown): Promise<unknown>;
package/dist/src/index.js CHANGED
@@ -331,11 +331,15 @@ export async function createAgentGateway(options) {
331
331
  canonicalBody.content.length > 0)
332
332
  throw new AgentGatewayError("OPENAI_FILE_INPUT_CONFLICT");
333
333
  }
334
- const actionSignal = AbortSignal.timeout(options.actionTimeoutMs ?? 45_000);
334
+ const actionTimeoutMs = Math.min(40_000, options.actionTimeoutMs ?? 40_000);
335
+ const propagationReserveMs = Math.min(1_500, Math.max(100, Math.floor(actionTimeoutMs / 10)));
336
+ const deadlineAt = new Date(now() + Math.max(1, actionTimeoutMs - propagationReserveMs)).toISOString();
337
+ const actionSignal = AbortSignal.timeout(actionTimeoutMs);
335
338
  const operation = action.uncertain && options.owners.lookupResult
336
339
  ? options.owners.lookupResult(action.operationId, authenticatedRoleRef, canonicalBody)
337
340
  : options.owners.route(action.operationId, authenticatedRoleRef, canonicalBody, {
338
341
  signal: actionSignal,
342
+ deadlineAt,
339
343
  ...(fileMaterializationInputs === undefined
340
344
  ? {}
341
345
  : { fileMaterializationInputs }),
@@ -12,6 +12,7 @@ export declare function createAgentGatewayProcess(input: {
12
12
  config: AgentGatewayProcessConfig;
13
13
  fetch?: typeof globalThis.fetch;
14
14
  log?: (entry: Record<string, unknown>) => void;
15
+ operationLog?: (entry: Record<string, unknown>) => void;
15
16
  }): Promise<Readonly<{
16
17
  readiness: () => Promise<{
17
18
  status: "NOT_READY" | "READY";
@@ -18,7 +18,20 @@ const safeDownstreamErrorCodes = new Set([
18
18
  "EXECUTION_GENERATION_MISMATCH",
19
19
  "EXECUTION_ROLE_SCOPE_MISMATCH",
20
20
  "EXECUTION_WORKER_SCOPE_MISMATCH",
21
+ "ROLE_TOOL_OPERATION_DENIED",
22
+ "DIRECT_TOOL_INPUT_INVALID",
23
+ "DIRECT_TOOL_DEADLINE_REQUIRED",
24
+ "LOCAL_TOOL_BRIDGE_UNAVAILABLE",
25
+ "LOCAL_TOOL_AUTH_INVALID",
26
+ "LOCAL_TOOL_INPUT_INVALID",
27
+ "LOCAL_TOOL_OFFLINE",
28
+ "LOCAL_TOOL_COMMAND_TIMEOUT",
29
+ "LOCAL_TOOL_RESULT_UNKNOWN",
30
+ "LOCAL_TOOL_PROVIDER_UNAVAILABLE",
31
+ "LOCAL_TOOL_SCOPE_DENIED",
32
+ "LOCAL_TOOL_COMMAND_FAILED",
21
33
  ]);
34
+ const directToolOperationIds = new Set(["localDev", "repomix", "codeGraph"]);
22
35
  async function boundedDownstreamErrorCode(response) {
23
36
  if (!response.body)
24
37
  return null;
@@ -70,6 +83,27 @@ function text(value, name) {
70
83
  throw new TypeError(`${name} must be a non-empty string`);
71
84
  return value;
72
85
  }
86
+ function logErrorCode(error) {
87
+ const value = error instanceof AgentGatewayError
88
+ ? error.code
89
+ : error &&
90
+ typeof error === "object" &&
91
+ typeof Reflect.get(error, "message") === "string"
92
+ ? String(Reflect.get(error, "message"))
93
+ : "GATEWAY_FAILURE";
94
+ return /^[A-Z][A-Z0-9_.:-]{0,159}$/.test(value)
95
+ ? value
96
+ : "GATEWAY_FAILURE";
97
+ }
98
+ function optionalToolOperation(value) {
99
+ if (typeof value !== "object" ||
100
+ value === null ||
101
+ Array.isArray(value) ||
102
+ typeof Reflect.get(value, "operation") !== "string")
103
+ return undefined;
104
+ const operation = String(Reflect.get(value, "operation"));
105
+ return /^[A-Za-z0-9_.:-]{1,160}$/.test(operation) ? operation : undefined;
106
+ }
73
107
  export function parseAgentGatewayProcessConfig(value) {
74
108
  const input = record(value, "agent-gateway config");
75
109
  const publicBaseUrl = new URL(text(input.publicBaseUrl, "publicBaseUrl"));
@@ -130,11 +164,6 @@ async function readDownstreamCredential(file) {
130
164
  export async function createAgentGatewayProcess(input) {
131
165
  const fetchImplementation = input.fetch ?? globalThis.fetch;
132
166
  const credentialFile = input.config.credentialFile;
133
- // Fail-fast at startup so a malformed configured credential store is rejected
134
- // before the process advertises readiness. Authentication re-reads the current
135
- // store on every attempt below, so a rotated key takes effect without a restart
136
- // and a malformed/half-written store fails closed instead of serving a stale
137
- // snapshot.
138
167
  await readCurrentCredentialStore(credentialFile);
139
168
  if (input.config.downstreamCredentialFile)
140
169
  await readDownstreamCredential(input.config.downstreamCredentialFile);
@@ -187,6 +216,29 @@ export async function createAgentGatewayProcess(input) {
187
216
  }
188
217
  return response.json();
189
218
  };
219
+ const emitOperation = (inputEvent) => {
220
+ const toolOperation = directToolOperationIds.has(inputEvent.operationId)
221
+ ? optionalToolOperation(inputEvent.value)
222
+ : undefined;
223
+ input.operationLog?.({
224
+ contract: "proflow.operation-boundary.v1",
225
+ timestamp: new Date().toISOString(),
226
+ source: "agent-gateway",
227
+ component: "agent-gateway-ingress",
228
+ event: inputEvent.mode === "LOOKUP"
229
+ ? "GATEWAY_ACTION_LOOKUP"
230
+ : "GATEWAY_ACTION",
231
+ boundary: directToolOperationIds.has(inputEvent.operationId)
232
+ ? "TOOL_INVOCATION"
233
+ : "ACTION",
234
+ status: inputEvent.status,
235
+ operationId: inputEvent.operationId,
236
+ roleRef: inputEvent.roleRef,
237
+ ...(toolOperation ? { toolOperation } : {}),
238
+ ...(inputEvent.errorCode ? { errorCode: inputEvent.errorCode } : {}),
239
+ durationMs: inputEvent.durationMs,
240
+ });
241
+ };
190
242
  const gateway = await createAgentGateway({
191
243
  host: input.config.host,
192
244
  port: input.config.port,
@@ -199,22 +251,68 @@ export async function createAgentGatewayProcess(input) {
199
251
  return roleRef;
200
252
  throw new Error("AUTHENTICATION_FAILED");
201
253
  },
202
- route(operationId, authenticatedRoleRef, value, context) {
203
- return downstream(`/actions/${encodeURIComponent(operationId)}`, {
204
- authenticatedRoleRef,
205
- input: value,
206
- ...(context?.fileMaterializationInputs === undefined
207
- ? {}
208
- : {
209
- fileMaterializationInputs: context.fileMaterializationInputs,
210
- }),
211
- }, context?.signal);
254
+ async route(operationId, authenticatedRoleRef, value, context) {
255
+ const started = performance.now();
256
+ try {
257
+ const result = await downstream(`/actions/${encodeURIComponent(operationId)}`, {
258
+ authenticatedRoleRef,
259
+ input: value,
260
+ deadlineAt: context?.deadlineAt,
261
+ ...(context?.fileMaterializationInputs === undefined
262
+ ? {}
263
+ : {
264
+ fileMaterializationInputs: context.fileMaterializationInputs,
265
+ }),
266
+ }, context?.signal);
267
+ emitOperation({
268
+ mode: "ROUTE",
269
+ operationId,
270
+ roleRef: authenticatedRoleRef,
271
+ value,
272
+ status: "SUCCEEDED",
273
+ durationMs: performance.now() - started,
274
+ });
275
+ return result;
276
+ }
277
+ catch (error) {
278
+ emitOperation({
279
+ mode: "ROUTE",
280
+ operationId,
281
+ roleRef: authenticatedRoleRef,
282
+ value,
283
+ status: "FAILED",
284
+ errorCode: logErrorCode(error),
285
+ durationMs: performance.now() - started,
286
+ });
287
+ throw error;
288
+ }
212
289
  },
213
- lookupResult(operationId, authenticatedRoleRef, value) {
214
- return downstream(`/actions/${encodeURIComponent(operationId)}/result`, {
215
- authenticatedRoleRef,
216
- input: value,
217
- });
290
+ async lookupResult(operationId, authenticatedRoleRef, value) {
291
+ const started = performance.now();
292
+ try {
293
+ const result = await downstream(`/actions/${encodeURIComponent(operationId)}/result`, { authenticatedRoleRef, input: value });
294
+ emitOperation({
295
+ mode: "LOOKUP",
296
+ operationId,
297
+ roleRef: authenticatedRoleRef,
298
+ value,
299
+ status: "SUCCEEDED",
300
+ durationMs: performance.now() - started,
301
+ });
302
+ return result;
303
+ }
304
+ catch (error) {
305
+ emitOperation({
306
+ mode: "LOOKUP",
307
+ operationId,
308
+ roleRef: authenticatedRoleRef,
309
+ value,
310
+ status: "FAILED",
311
+ errorCode: logErrorCode(error),
312
+ durationMs: performance.now() - started,
313
+ });
314
+ throw error;
315
+ }
218
316
  },
219
317
  async readiness() {
220
318
  try {
@@ -247,6 +345,7 @@ export async function createAgentGatewayProcess(input) {
247
345
  timestamp: new Date().toISOString(),
248
346
  component: "agent-gateway-process",
249
347
  event: "SERVICE_STARTED",
348
+ status: "SUCCEEDED",
250
349
  ...address,
251
350
  });
252
351
  return address;
@@ -257,6 +356,7 @@ export async function createAgentGatewayProcess(input) {
257
356
  timestamp: new Date().toISOString(),
258
357
  component: "agent-gateway-process",
259
358
  event: "SERVICE_STOPPED",
359
+ status: "SUCCEEDED",
260
360
  });
261
361
  };
262
362
  return Object.freeze({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tomflow/proflow-agent-gateway",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "type": "module",
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -28,15 +28,14 @@
28
28
  "@tomflow/proflow-module-contract": "^0.1.13"
29
29
  },
30
30
  "devDependencies": {
31
- "@tomflow/proflow-agent-runtime": "^0.1.13",
31
+ "@tomflow/proflow-agent-runtime": "^0.1.17",
32
+ "@tomflow/proflow-execution-browser-extension": "^0.1.58",
33
+ "@tomflow/proflow-execution-runtime": "^0.1.19",
34
+ "@tomflow/proflow-platform-host": "^0.1.28",
32
35
  "@tomflow/proflow-deployment-conformance": "^0.1.13",
33
- "@tomflow/proflow-execution-browser-extension": "^0.1.45",
34
- "@tomflow/proflow-execution-runtime": "^0.1.18",
35
- "@tomflow/proflow-task-orchestration": "^0.1.11",
36
- "@tomflow/proflow-task-store-sqlite": "^0.1.11",
37
36
  "@tomflow/proflow-task-migration-runner": "^0.1.11",
38
- "@tomflow/proflow-execution-local": "^0.1.15",
39
- "@tomflow/proflow-platform-host": "^0.1.19"
37
+ "@tomflow/proflow-task-orchestration": "^0.1.11",
38
+ "@tomflow/proflow-task-store-sqlite": "^0.1.12"
40
39
  },
41
40
  "description": "The sole Custom GPT Actions HTTP ingress and OpenAI transport anti-corruption layer.",
42
41
  "keywords": [
@@ -3,7 +3,7 @@
3
3
  "contractVersion": "1.0.0",
4
4
  "moduleRef": "agent-gateway",
5
5
  "packageName": "@tomflow/proflow-agent-gateway",
6
- "moduleVersion": "0.1.16",
6
+ "moduleVersion": "0.1.18",
7
7
  "kind": "service",
8
8
  "templateVersion": "1.0.0",
9
9
  "platformCompatibility": ">=1.0.0 <2.0.0",
@@ -26,10 +26,6 @@
26
26
  "contractRef": "task-orchestration",
27
27
  "versionRange": ">=1.0.0 <2.0.0"
28
28
  },
29
- {
30
- "contractRef": "execution",
31
- "versionRange": ">=1.0.0 <2.0.0"
32
- },
33
29
  {
34
30
  "contractRef": "platform-host",
35
31
  "versionRange": ">=1.0.0 <2.0.0"