@zackbart/connecta 0.18.2 → 0.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +100 -4
- package/README.md +4 -0
- package/dist/catalog-service.d.ts +20 -13
- package/dist/catalog-service.js +123 -116
- package/dist/catalog.js +29 -46
- package/dist/connector-scope.js +2 -7
- package/dist/connectors/api.d.ts +4 -16
- package/dist/connectors/api.js +19 -46
- package/dist/connectors/guarded-fetch.d.ts +9 -23
- package/dist/connectors/guarded-fetch.js +38 -76
- package/dist/connectors/remote-mcp.js +36 -79
- package/dist/errors.d.ts +6 -27
- package/dist/errors.js +8 -5
- package/dist/execute.d.ts +24 -22
- package/dist/execute.js +98 -145
- package/dist/executor-result.d.ts +1 -0
- package/dist/executor-result.js +4 -11
- package/dist/executors/quickjs-child.js +1 -3
- package/dist/executors/quickjs-runtime.js +1 -3
- package/dist/executors/quickjs.js +1 -3
- package/dist/index.js +27 -57
- package/dist/invocation.js +114 -178
- package/dist/meta-tools.d.ts +15 -28
- package/dist/meta-tools.js +33 -89
- package/dist/providers/cloudflare.d.ts +2 -18
- package/dist/providers/cloudflare.js +1460 -2451
- package/dist/providers/linear.d.ts +4 -41
- package/dist/providers/linear.js +8 -39
- package/dist/providers/mixpanel.d.ts +3 -25
- package/dist/providers/mixpanel.js +7 -22
- package/dist/providers/notion.d.ts +1 -15
- package/dist/providers/notion.js +44 -173
- package/dist/providers/revenuecat.d.ts +4 -57
- package/dist/providers/revenuecat.js +10 -93
- package/dist/providers/stripe.d.ts +1 -12
- package/dist/providers/stripe.js +7 -45
- package/dist/registry.d.ts +16 -34
- package/dist/registry.js +18 -103
- package/dist/result-shapes.d.ts +13 -0
- package/dist/result-shapes.js +331 -0
- package/dist/routes/mcp.js +1 -1
- package/dist/routes/oauth.js +3 -3
- package/dist/routes/shared.d.ts +15 -15
- package/dist/routes/shared.js +1 -3
- package/dist/skills.js +3 -3
- package/dist/timeout.d.ts +8 -7
- package/dist/timeout.js +47 -38
- package/dist/types.d.ts +3 -3
- package/dist/ui.d.ts +1 -25
- package/dist/ui.js +18 -45
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/documentation/architecture.md +5 -2
- package/documentation/call-admission.md +1 -1
- package/documentation/cloudflare.md +1 -1
- package/documentation/code-mode.md +13 -13
- package/documentation/connectors.md +34 -1
- package/documentation/linear.md +1 -1
- package/documentation/meta-tools.md +17 -3
- package/documentation/mixpanel.md +1 -1
- package/documentation/notion.md +1 -1
- package/documentation/operations.md +20 -15
- package/documentation/provider-conventions.md +1 -1
- package/documentation/revenuecat.md +1 -1
- package/documentation/stripe.md +1 -1
- package/documentation/upgrading.md +24 -4
- package/ethos.md +74 -120
- package/package.json +3 -4
- package/templates/node/package.json +1 -1
- package/documentation/code-first-exploration.md +0 -292
- package/documentation/mcp-2026-07-28.md +0 -46
- package/documentation/mcp-ui-design.md +0 -382
- package/documentation/program-ui-read-calls.md +0 -213
- package/documentation/provider-audit.md +0 -198
- package/documentation/rich-output-design.md +0 -211
package/dist/execute.js
CHANGED
|
@@ -4,7 +4,7 @@ import { boundedDiscoveryText, CatalogService, DiscoveryPolicyError, flatSearchR
|
|
|
4
4
|
import { errorResult, jsonResult } from "./meta-tools.js";
|
|
5
5
|
import { guardExecuteResultValue, MAX_EXECUTE_LOG_CHARS, truncateExecuteText, } from "./executor-result.js";
|
|
6
6
|
import { ExecutorAdmissionError, ExecutorExecutionError, isAdmittingExecutor, } from "./executor-admission.js";
|
|
7
|
-
import { boundedEchoText, classifyCallError } from "./errors.js";
|
|
7
|
+
import { boundedEchoText, classifyCallError, msg } from "./errors.js";
|
|
8
8
|
import { InvocationFailure, InvocationService, } from "./invocation.js";
|
|
9
9
|
import { hasConnectorGuides } from "./skills.js";
|
|
10
10
|
import { isExplicitlyReadOnly } from "./tool-safety.js";
|
|
@@ -306,15 +306,19 @@ export class EmitCollector {
|
|
|
306
306
|
* complaint about its type would send the author to fix the wrong thing.
|
|
307
307
|
*/
|
|
308
308
|
acceptUi(...values) {
|
|
309
|
-
|
|
310
|
-
throw guestFailure("invalid_args", "connecta.ui accepts at most one payload per run: a view was already accepted and stands");
|
|
311
|
-
}
|
|
309
|
+
this.assertUiVacant();
|
|
312
310
|
this.acceptUiPayload(requireUiPayload(values));
|
|
313
311
|
}
|
|
314
|
-
|
|
312
|
+
acceptValidatedUi(payload) {
|
|
313
|
+
this.assertUiVacant();
|
|
314
|
+
this.acceptUiPayload(payload);
|
|
315
|
+
}
|
|
316
|
+
assertUiVacant() {
|
|
315
317
|
if (this.ui) {
|
|
316
318
|
throw guestFailure("invalid_args", "connecta.ui accepts at most one payload per run: a view was already accepted and stands");
|
|
317
319
|
}
|
|
320
|
+
}
|
|
321
|
+
acceptUiPayload(payload) {
|
|
318
322
|
let serialized;
|
|
319
323
|
try {
|
|
320
324
|
serialized = JSON.stringify(payload);
|
|
@@ -429,9 +433,6 @@ const SANDBOX_RESERVED_NAMES = new Set([
|
|
|
429
433
|
"__stringifyForCodemode",
|
|
430
434
|
"__parseForCodemode",
|
|
431
435
|
]);
|
|
432
|
-
function msg(err) {
|
|
433
|
-
return err instanceof Error ? err.message : String(err);
|
|
434
|
-
}
|
|
435
436
|
function guestFailure(code, message, retryable = false) {
|
|
436
437
|
return new InvocationFailure({ code, message, retryable });
|
|
437
438
|
}
|
|
@@ -515,13 +516,9 @@ export async function buildSandboxProviders(registry, baseUrl, logger, activity,
|
|
|
515
516
|
requestScope,
|
|
516
517
|
// A program that just missed an address cannot call search_tools.
|
|
517
518
|
searchRoute: "connecta.search",
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
...(limits.probeTimeoutMs !== undefined
|
|
522
|
-
? { probeTimeoutMs: limits.probeTimeoutMs }
|
|
523
|
-
: {}),
|
|
524
|
-
...(limits.defer !== undefined ? { defer: limits.defer } : {}),
|
|
519
|
+
concurrency: limits.discoveryConcurrency,
|
|
520
|
+
probeTimeoutMs: limits.probeTimeoutMs,
|
|
521
|
+
defer: limits.defer,
|
|
525
522
|
});
|
|
526
523
|
const invocation = new InvocationService(registry, catalog, activity);
|
|
527
524
|
const maxHostCalls = Math.max(1, Math.trunc(limits.maxHostCalls ?? EXECUTE_MAX_HOST_CALLS));
|
|
@@ -573,23 +570,30 @@ export async function buildSandboxProviders(registry, baseUrl, logger, activity,
|
|
|
573
570
|
throw err;
|
|
574
571
|
}
|
|
575
572
|
};
|
|
576
|
-
const
|
|
577
|
-
const
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
573
|
+
const timedCatalog = async (operation, fn) => {
|
|
574
|
+
const started = Date.now();
|
|
575
|
+
try {
|
|
576
|
+
const result = await fn();
|
|
577
|
+
limits.diagnostics?.recordCatalog(operation, Date.now() - started, true, result);
|
|
578
|
+
return result;
|
|
582
579
|
}
|
|
580
|
+
catch (err) {
|
|
581
|
+
limits.diagnostics?.recordCatalog(operation, Date.now() - started, false);
|
|
582
|
+
throw err;
|
|
583
|
+
}
|
|
584
|
+
};
|
|
585
|
+
const called = async (operation, invoke) => {
|
|
586
|
+
const outcome = await invoke();
|
|
587
|
+
limits.diagnostics?.recordCall(operation, outcome);
|
|
588
|
+
if (!outcome.ok)
|
|
589
|
+
throw new InvocationFailure(outcome.error);
|
|
583
590
|
return outcome.value;
|
|
584
591
|
};
|
|
592
|
+
const callAddress = async (address, args, diagnosticOperation = "call") => {
|
|
593
|
+
return called(diagnosticOperation, () => invocation.invoke(String(address), args ?? {}, invocationContext()));
|
|
594
|
+
};
|
|
585
595
|
const callNamespace = async (connectorId, toolAlias, args) => {
|
|
586
|
-
|
|
587
|
-
limits.diagnostics?.recordCall("call", outcome);
|
|
588
|
-
if (!outcome.ok) {
|
|
589
|
-
const failure = new InvocationFailure(outcome.error);
|
|
590
|
-
throw failure;
|
|
591
|
-
}
|
|
592
|
-
return outcome.value;
|
|
596
|
+
return called("call", () => invocation.invokeToolAlias(String(connectorId), String(toolAlias), sanitizeIdentifier, args ?? {}, invocationContext()));
|
|
593
597
|
};
|
|
594
598
|
/**
|
|
595
599
|
* A read binding is admitted while the program still owns the request. The
|
|
@@ -643,7 +647,7 @@ export async function buildSandboxProviders(registry, baseUrl, logger, activity,
|
|
|
643
647
|
throw guestFailure("unavailable", "connecta.ui is unavailable: no emission collector was configured for this execution", true);
|
|
644
648
|
}
|
|
645
649
|
const payload = await validateUiReads(requireUiPayload(values));
|
|
646
|
-
limits.emitCollector.
|
|
650
|
+
limits.emitCollector.acceptValidatedUi(payload);
|
|
647
651
|
},
|
|
648
652
|
batch: async (calls) => {
|
|
649
653
|
const started = Date.now();
|
|
@@ -684,43 +688,21 @@ export async function buildSandboxProviders(registry, baseUrl, logger, activity,
|
|
|
684
688
|
throw err;
|
|
685
689
|
}
|
|
686
690
|
},
|
|
687
|
-
search: async (raw) => {
|
|
688
|
-
const
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
catch (err) {
|
|
703
|
-
limits.diagnostics?.recordCatalog("search", Date.now() - started, false);
|
|
704
|
-
throw err;
|
|
705
|
-
}
|
|
706
|
-
},
|
|
707
|
-
describe: async (raw) => {
|
|
708
|
-
const started = Date.now();
|
|
709
|
-
try {
|
|
710
|
-
const result = await typedDiscovery(async () => {
|
|
711
|
-
const args = (raw ?? {});
|
|
712
|
-
const result = { tools: await catalog.describe(args) };
|
|
713
|
-
boundedDiscoveryText(result, 'Split the address list or use format: "compact".');
|
|
714
|
-
return result;
|
|
715
|
-
});
|
|
716
|
-
limits.diagnostics?.recordCatalog("describe", Date.now() - started, true, result);
|
|
717
|
-
return result;
|
|
718
|
-
}
|
|
719
|
-
catch (err) {
|
|
720
|
-
limits.diagnostics?.recordCatalog("describe", Date.now() - started, false);
|
|
721
|
-
throw err;
|
|
722
|
-
}
|
|
723
|
-
},
|
|
691
|
+
search: async (raw) => timedCatalog("search", () => typedDiscovery(async () => {
|
|
692
|
+
const args = (raw ?? {});
|
|
693
|
+
const result = flatSearchResult(await catalog.search({
|
|
694
|
+
...args,
|
|
695
|
+
includeSchemaKeys: args.includeSchemaKeys !== false,
|
|
696
|
+
}));
|
|
697
|
+
boundedDiscoveryText(result, "Request a smaller limit, omit fullDescriptions, use compact schemas, or pass includeSchemaKeys: false.");
|
|
698
|
+
return result;
|
|
699
|
+
})),
|
|
700
|
+
describe: async (raw) => timedCatalog("describe", () => typedDiscovery(async () => {
|
|
701
|
+
const args = (raw ?? {});
|
|
702
|
+
const result = { tools: await catalog.describe(args) };
|
|
703
|
+
boundedDiscoveryText(result, 'Split the address list or use format: "compact".');
|
|
704
|
+
return result;
|
|
705
|
+
})),
|
|
724
706
|
};
|
|
725
707
|
const transportedFns = Object.fromEntries(Object.entries(fns).map(([name, fn]) => [
|
|
726
708
|
name,
|
|
@@ -790,13 +772,9 @@ export function createExecuteTool(registry, baseUrl, executor, logger, activity,
|
|
|
790
772
|
},
|
|
791
773
|
emitCollector: emitted,
|
|
792
774
|
...(diagnostics ? { diagnostics } : {}),
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
...(config.probeTimeoutMs !== undefined
|
|
797
|
-
? { probeTimeoutMs: config.probeTimeoutMs }
|
|
798
|
-
: {}),
|
|
799
|
-
...(config.defer !== undefined ? { defer: config.defer } : {}),
|
|
775
|
+
discoveryConcurrency: config.discoveryConcurrency,
|
|
776
|
+
probeTimeoutMs: config.probeTimeoutMs,
|
|
777
|
+
defer: config.defer,
|
|
800
778
|
});
|
|
801
779
|
}
|
|
802
780
|
finally {
|
|
@@ -826,8 +804,10 @@ export function createExecuteTool(registry, baseUrl, executor, logger, activity,
|
|
|
826
804
|
retryAfterMs: err.retryAfterMs,
|
|
827
805
|
});
|
|
828
806
|
}
|
|
829
|
-
|
|
830
|
-
|
|
807
|
+
return failureResponse(err.message, {
|
|
808
|
+
emitted: err instanceof ExecutorExecutionError ? emitted : undefined,
|
|
809
|
+
diagnostics,
|
|
810
|
+
code: {
|
|
831
811
|
code: err.code,
|
|
832
812
|
message: err.message,
|
|
833
813
|
retryable: err.retryable,
|
|
@@ -835,28 +815,13 @@ export function createExecuteTool(registry, baseUrl, executor, logger, activity,
|
|
|
835
815
|
? { retryAfterMs: err.retryAfterMs }
|
|
836
816
|
: {}),
|
|
837
817
|
},
|
|
838
|
-
...(err instanceof ExecutorExecutionError
|
|
839
|
-
? discardedEmits(emitted)
|
|
840
|
-
: {}),
|
|
841
|
-
...(diagnostics ? { diagnostics: diagnostics.finish() } : {}),
|
|
842
818
|
});
|
|
843
|
-
result.isError = true;
|
|
844
|
-
return result;
|
|
845
819
|
}
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
retryable: false,
|
|
852
|
-
},
|
|
853
|
-
...discardedEmits(emitted),
|
|
854
|
-
diagnostics: diagnostics.finish(),
|
|
855
|
-
});
|
|
856
|
-
result.isError = true;
|
|
857
|
-
return result;
|
|
858
|
-
}
|
|
859
|
-
return errorResult(`Executor failed: ${msg(err)}${discardedEmitsText(emitted)}`);
|
|
820
|
+
return failureResponse(`Executor failed: ${msg(err)}`, {
|
|
821
|
+
emitted,
|
|
822
|
+
diagnostics,
|
|
823
|
+
code: "executor_failed",
|
|
824
|
+
});
|
|
860
825
|
}
|
|
861
826
|
finally {
|
|
862
827
|
// A sandbox timeout or early return must also release any outstanding
|
|
@@ -900,31 +865,20 @@ export function createExecuteTool(registry, baseUrl, executor, logger, activity,
|
|
|
900
865
|
// framed with bounded caller text (`boundedEchoText`) precisely so
|
|
901
866
|
// this path never needs one. Adding a cap here instead would leave the
|
|
902
867
|
// top-level surfaces, which have the same amplification, uncovered.
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
868
|
+
return failureResponse(invocationFailure.details.message, {
|
|
869
|
+
logs,
|
|
870
|
+
emitted,
|
|
871
|
+
diagnostics,
|
|
872
|
+
code: invocationFailure.details,
|
|
908
873
|
});
|
|
909
|
-
result.isError = true;
|
|
910
|
-
return result;
|
|
911
874
|
}
|
|
912
875
|
const message = `Error: ${outcome.error}`;
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
},
|
|
920
|
-
...(logs ? { logs } : {}),
|
|
921
|
-
...discardedEmits(emitted),
|
|
922
|
-
diagnostics: diagnostics.finish(),
|
|
923
|
-
});
|
|
924
|
-
result.isError = true;
|
|
925
|
-
return result;
|
|
926
|
-
}
|
|
927
|
-
return errorResult(`${message}${logs ? `\n\nLogs:\n${logs}` : ""}${discardedEmitsText(emitted)}`);
|
|
876
|
+
return failureResponse(message, {
|
|
877
|
+
logs,
|
|
878
|
+
emitted,
|
|
879
|
+
diagnostics,
|
|
880
|
+
code: "executor_failed",
|
|
881
|
+
});
|
|
928
882
|
}
|
|
929
883
|
// A result crossing back as a host BigInt (or otherwise unserializable
|
|
930
884
|
// value) makes JSON.stringify throw — keep that inside the structured
|
|
@@ -935,21 +889,12 @@ export function createExecuteTool(registry, baseUrl, executor, logger, activity,
|
|
|
935
889
|
}
|
|
936
890
|
catch (err) {
|
|
937
891
|
const message = `Error: result is not JSON-serializable: ${msg(err)}`;
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
},
|
|
945
|
-
...(logs ? { logs } : {}),
|
|
946
|
-
...discardedEmits(emitted),
|
|
947
|
-
diagnostics: diagnostics.finish(),
|
|
948
|
-
});
|
|
949
|
-
response.isError = true;
|
|
950
|
-
return response;
|
|
951
|
-
}
|
|
952
|
-
return errorResult(`${message}${logs ? `\n\nLogs:\n${logs}` : ""}${discardedEmitsText(emitted)}`);
|
|
892
|
+
return failureResponse(message, {
|
|
893
|
+
logs,
|
|
894
|
+
emitted,
|
|
895
|
+
diagnostics,
|
|
896
|
+
code: "executor_failed",
|
|
897
|
+
});
|
|
953
898
|
}
|
|
954
899
|
const response = jsonResult({
|
|
955
900
|
result,
|
|
@@ -976,6 +921,22 @@ export function createExecuteTool(registry, baseUrl, executor, logger, activity,
|
|
|
976
921
|
return response;
|
|
977
922
|
};
|
|
978
923
|
}
|
|
924
|
+
function failureResponse(message, options) {
|
|
925
|
+
const { logs, emitted, diagnostics, code } = options;
|
|
926
|
+
if (diagnostics || typeof code !== "string") {
|
|
927
|
+
const result = jsonResult({
|
|
928
|
+
error: typeof code === "string"
|
|
929
|
+
? { code, message, retryable: false }
|
|
930
|
+
: code,
|
|
931
|
+
...(logs ? { logs } : {}),
|
|
932
|
+
...(emitted ? discardedEmits(emitted) : {}),
|
|
933
|
+
...(diagnostics ? { diagnostics: diagnostics.finish() } : {}),
|
|
934
|
+
});
|
|
935
|
+
result.isError = true;
|
|
936
|
+
return result;
|
|
937
|
+
}
|
|
938
|
+
return errorResult(`${message}${logs ? `\n\nLogs:\n${logs}` : ""}${emitted ? discardedEmitsText(emitted) : ""}`);
|
|
939
|
+
}
|
|
979
940
|
/**
|
|
980
941
|
* M4 and U3: a failed program delivers no blocks and no view, but each
|
|
981
942
|
* discard is visible — and one failure can discard both.
|
|
@@ -1049,15 +1010,11 @@ export function registerExecuteTool(server, registry, ctx) {
|
|
|
1049
1010
|
};
|
|
1050
1011
|
const connectors = registry.listConnectors();
|
|
1051
1012
|
const handler = createExecuteTool(registry, ctx.baseUrl, ctx.executor, ctx.logger, ctx.activity, {
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
: {}),
|
|
1055
|
-
...(ctx.probeTimeoutMs !== undefined
|
|
1056
|
-
? { probeTimeoutMs: ctx.probeTimeoutMs }
|
|
1057
|
-
: {}),
|
|
1013
|
+
discoveryConcurrency: ctx.discoveryConcurrency,
|
|
1014
|
+
probeTimeoutMs: ctx.probeTimeoutMs,
|
|
1058
1015
|
maxEmittedBytes: emitBudgets.maxBytes,
|
|
1059
1016
|
maxEmittedBlocks: emitBudgets.maxBlocks,
|
|
1060
|
-
|
|
1017
|
+
defer: ctx.defer,
|
|
1061
1018
|
});
|
|
1062
1019
|
server.registerTool("execute_code", {
|
|
1063
1020
|
description: executeDescription(emitBudgets, hasConnectorGuides(connectors), connectors),
|
|
@@ -1078,12 +1035,8 @@ export function registerExecuteTool(server, registry, ctx) {
|
|
|
1078
1035
|
destructiveHint: false,
|
|
1079
1036
|
openWorldHint: true,
|
|
1080
1037
|
},
|
|
1081
|
-
// U5 and U10
|
|
1082
|
-
//
|
|
1083
|
-
// is the text fallback the spec mandates — and a stateless aggregator
|
|
1084
|
-
// has nowhere dependable to hold a negotiation check anyway. The
|
|
1085
|
-
// explicit visibility keeps hosts from being told the view may call
|
|
1086
|
-
// execute_code; the default ["model","app"] would say exactly that.
|
|
1038
|
+
// U5 and U10 are specified in documentation/code-mode.md; explicit model
|
|
1039
|
+
// visibility prevents hosts from offering execute_code to the view.
|
|
1087
1040
|
_meta: {
|
|
1088
1041
|
ui: {
|
|
1089
1042
|
resourceUri: PROGRAM_UI_RESOURCE_URI,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ExecuteResult } from "./types.js";
|
|
2
2
|
export declare const MAX_EXECUTE_LOG_CHARS = 4000;
|
|
3
|
+
export declare function serializeResultText(value: unknown): string;
|
|
3
4
|
export declare function guardExecuteResultValue(value: unknown): unknown;
|
|
4
5
|
export declare function truncateExecuteText(text: string, max: number): string;
|
|
5
6
|
/**
|
package/dist/executor-result.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
+
import { msg } from "./errors.js";
|
|
1
2
|
/** ~6k tokens. Sandbox code should filter data down before returning. */
|
|
2
3
|
const MAX_EXECUTE_RESULT_CHARS = 24_000;
|
|
3
4
|
export const MAX_EXECUTE_LOG_CHARS = 4_000;
|
|
4
|
-
function
|
|
5
|
-
return err instanceof Error ? err.message : String(err);
|
|
6
|
-
}
|
|
7
|
-
function serializeExecuteValue(value) {
|
|
5
|
+
export function serializeResultText(value) {
|
|
8
6
|
const serialized = JSON.stringify(value);
|
|
9
7
|
return serialized === undefined ? String(value) : serialized;
|
|
10
8
|
}
|
|
@@ -37,15 +35,10 @@ function truncationEnvelope(text) {
|
|
|
37
35
|
// the overshoot ratio (minus a step) strictly shrinks the budget.
|
|
38
36
|
budget = Math.max(0, Math.floor(budget * (MAX_EXECUTE_RESULT_CHARS / size)) - 8);
|
|
39
37
|
}
|
|
40
|
-
|
|
41
|
-
// unchecked slice is exactly how a "bounded" envelope stops being bounded.
|
|
42
|
-
const clamped = { ...base, preview: text.slice(0, Math.max(0, budget)) };
|
|
43
|
-
return JSON.stringify(clamped).length <= MAX_EXECUTE_RESULT_CHARS
|
|
44
|
-
? clamped
|
|
45
|
-
: { ...base, preview: "" };
|
|
38
|
+
return { ...base, preview: text.slice(0, budget) };
|
|
46
39
|
}
|
|
47
40
|
export function guardExecuteResultValue(value) {
|
|
48
|
-
const text =
|
|
41
|
+
const text = serializeResultText(value);
|
|
49
42
|
if (text.length <= MAX_EXECUTE_RESULT_CHARS)
|
|
50
43
|
return value;
|
|
51
44
|
return truncationEnvelope(text);
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import { prepareExecuteResultForTransport } from "../executor-result.js";
|
|
2
|
+
import { msg } from "../errors.js";
|
|
2
3
|
import { MAX_QUICKJS_IPC_BYTES, MAX_QUICKJS_HOST_RPC_BYTES, serializedBytes, stringifyBounded, } from "./quickjs-protocol.js";
|
|
3
4
|
import { executeQuickJs, prepareQuickJs } from "./quickjs-runtime.js";
|
|
4
5
|
let activeJobId;
|
|
5
6
|
let nextCallId = 1;
|
|
6
7
|
const pending = new Map();
|
|
7
|
-
function msg(err) {
|
|
8
|
-
return err instanceof Error ? err.message : String(err);
|
|
9
|
-
}
|
|
10
8
|
function send(message) {
|
|
11
9
|
if (!process.send)
|
|
12
10
|
throw new Error("QuickJS child IPC channel is unavailable.");
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
// continuations from a host-side loop. Values cross the boundary as JSON, so
|
|
9
9
|
// provider args/results must be JSON-serializable.
|
|
10
10
|
import { getQuickJS, } from "quickjs-emscripten";
|
|
11
|
+
import { msg } from "../errors.js";
|
|
11
12
|
import { hostCallLabel, MAX_QUICKJS_LOG_TRANSPORT_BYTES, serializedBytes, } from "./quickjs-protocol.js";
|
|
12
13
|
/** Load and compile the shared QuickJS WASM module before a run budget starts. */
|
|
13
14
|
export async function prepareQuickJs() {
|
|
@@ -29,9 +30,6 @@ const MAX_LOG_MARKER_TRANSPORT_BYTES = Math.max(logTransportBytes(LOG_ENTRY_LIMI
|
|
|
29
30
|
// This still lets guest code reduce data more than ten times larger than
|
|
30
31
|
// connecta's final response budget.
|
|
31
32
|
const MAX_HOST_RESULT_BYTES = 256 * 1024;
|
|
32
|
-
function msg(err) {
|
|
33
|
-
return err instanceof Error ? err.message : String(err);
|
|
34
|
-
}
|
|
35
33
|
function logTransportBytes(entry) {
|
|
36
34
|
// The log is encoded into ExecutionPayload, then that payloadJson string is
|
|
37
35
|
// encoded into ChildToParentMessage. Measure the units the IPC cap sees.
|
|
@@ -6,6 +6,7 @@ import { fork } from "node:child_process";
|
|
|
6
6
|
import { existsSync } from "node:fs";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
8
|
import { AdmissionController, ExecutorAdmissionError, ExecutorExecutionError, } from "../executor-admission.js";
|
|
9
|
+
import { msg } from "../errors.js";
|
|
9
10
|
import { hostCallLabel, MAX_QUICKJS_IPC_BYTES, MAX_QUICKJS_HOST_RPC_BYTES, serializedBytes, stringifyBounded, } from "./quickjs-protocol.js";
|
|
10
11
|
export { normalizeCode } from "./quickjs-runtime.js";
|
|
11
12
|
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
@@ -19,9 +20,6 @@ const CHILD_EXIT_GRACE_MS = 250;
|
|
|
19
20
|
const CHILD_STARTUP_TIMEOUT_MS = 10_000;
|
|
20
21
|
const MAX_CHILD_STDERR_BYTES = 8 * 1024;
|
|
21
22
|
const MAX_ERROR_CHARS = 4_000;
|
|
22
|
-
function msg(err) {
|
|
23
|
-
return err instanceof Error ? err.message : String(err);
|
|
24
|
-
}
|
|
25
23
|
function retainStderrTail(current, chunk) {
|
|
26
24
|
const incoming = typeof chunk === "string" ? Buffer.from(chunk) : chunk;
|
|
27
25
|
if (incoming.length >= MAX_CHILD_STDERR_BYTES) {
|
package/dist/index.js
CHANGED
|
@@ -194,7 +194,7 @@ function warnInsecureConfig(config, inboundAuth, logger) {
|
|
|
194
194
|
}
|
|
195
195
|
export function createConnecta(config) {
|
|
196
196
|
assertNoLegacyConfig(config);
|
|
197
|
-
if (
|
|
197
|
+
if (hasOwn(config, "surface")) {
|
|
198
198
|
throw new Error("ConnectaConfig.surface was removed in issue #273. Remove it; connecta " +
|
|
199
199
|
"now serves one seven-tool surface.");
|
|
200
200
|
}
|
|
@@ -233,30 +233,20 @@ export function createConnecta(config) {
|
|
|
233
233
|
const registry = new Registry(config.connectors, {
|
|
234
234
|
storage,
|
|
235
235
|
logger,
|
|
236
|
-
|
|
237
|
-
|
|
236
|
+
credentialVault,
|
|
237
|
+
catalogDriftActivity: config.activity?.store
|
|
238
238
|
? {
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
: {}),
|
|
245
|
-
},
|
|
239
|
+
sink: config.activity.store,
|
|
240
|
+
serverInfo,
|
|
241
|
+
...(config.activity.deploymentId !== undefined
|
|
242
|
+
? { deploymentId: config.activity.deploymentId }
|
|
243
|
+
: {}),
|
|
246
244
|
}
|
|
247
|
-
:
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
? { persistToolCatalog: config.discovery.persistCatalog }
|
|
253
|
-
: {}),
|
|
254
|
-
...(config.discovery?.staleCatalogSeconds !== undefined
|
|
255
|
-
? { toolCatalogStaleSeconds: config.discovery.staleCatalogSeconds }
|
|
256
|
-
: {}),
|
|
257
|
-
...(config.calls?.maxResultBytes !== undefined
|
|
258
|
-
? { maxResultBytes: config.calls.maxResultBytes }
|
|
259
|
-
: {}),
|
|
245
|
+
: undefined,
|
|
246
|
+
toolCacheTtlSeconds: config.discovery?.catalogTtlSeconds,
|
|
247
|
+
persistToolCatalog: config.discovery?.persistCatalog,
|
|
248
|
+
toolCatalogStaleSeconds: config.discovery?.staleCatalogSeconds,
|
|
249
|
+
maxResultBytes: config.calls?.maxResultBytes,
|
|
260
250
|
});
|
|
261
251
|
const inboundAuth = normalizeAuth(accessTokens ? [accessTokens.auth, ...configuredAuth] : configuredAuth);
|
|
262
252
|
warnInsecureConfig(config, inboundAuth, logger);
|
|
@@ -279,44 +269,24 @@ export function createConnecta(config) {
|
|
|
279
269
|
const handler = createFetchHandler({
|
|
280
270
|
registry,
|
|
281
271
|
auth: inboundAuth,
|
|
282
|
-
|
|
272
|
+
publicUrl: config.publicUrl,
|
|
283
273
|
serverInfo,
|
|
284
274
|
logger,
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
...(config.activity?.readGate !== undefined
|
|
289
|
-
? { activityReadGate: config.activity.readGate }
|
|
290
|
-
: {}),
|
|
291
|
-
...(config.activity?.deploymentId !== undefined
|
|
292
|
-
? { activityDeploymentId: config.activity.deploymentId }
|
|
293
|
-
: {}),
|
|
275
|
+
activity: config.activity?.store,
|
|
276
|
+
activityReadGate: config.activity?.readGate,
|
|
277
|
+
activityDeploymentId: config.activity?.deploymentId,
|
|
294
278
|
executor,
|
|
295
|
-
|
|
296
|
-
? { executorName: configuredExecutorName }
|
|
297
|
-
: {}),
|
|
279
|
+
executorName: configuredExecutorName,
|
|
298
280
|
requestAdmission,
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
...(config.execute?.maxEmittedBytes !== undefined
|
|
309
|
-
? { maxEmittedBytes: config.execute.maxEmittedBytes }
|
|
310
|
-
: {}),
|
|
311
|
-
...(config.execute?.maxEmittedBlocks !== undefined
|
|
312
|
-
? { maxEmittedBlocks: config.execute.maxEmittedBlocks }
|
|
313
|
-
: {}),
|
|
314
|
-
...(credentialVault !== undefined ? { credentialVault } : {}),
|
|
315
|
-
...(accessTokens !== undefined ? { accessTokens } : {}),
|
|
316
|
-
...(config.deploymentInfo !== undefined
|
|
317
|
-
? { deploymentInfo: config.deploymentInfo }
|
|
318
|
-
: {}),
|
|
319
|
-
...(config.branding !== undefined ? { branding: config.branding } : {}),
|
|
281
|
+
defaultToolTimeoutMs: config.calls?.defaultTimeoutMs,
|
|
282
|
+
probeTimeoutMs: config.discovery?.probeTimeoutMs,
|
|
283
|
+
discoveryConcurrency: config.discovery?.concurrency,
|
|
284
|
+
maxEmittedBytes: config.execute?.maxEmittedBytes,
|
|
285
|
+
maxEmittedBlocks: config.execute?.maxEmittedBlocks,
|
|
286
|
+
credentialVault,
|
|
287
|
+
accessTokens,
|
|
288
|
+
deploymentInfo: config.deploymentInfo,
|
|
289
|
+
branding: config.branding,
|
|
320
290
|
});
|
|
321
291
|
let closePromise;
|
|
322
292
|
return {
|