@zackbart/connecta 0.39.0 → 0.41.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 +115 -0
- package/bin/connecta.mjs +4 -8
- package/bin/version.mjs +1 -1
- package/dist/access-tokens.js +6 -4
- package/dist/activity.d.ts +30 -4
- package/dist/activity.js +28 -2
- package/dist/agent-observation.js +24 -2
- package/dist/agent-routes.js +4 -41
- package/dist/auth/clerk.d.ts +6 -1
- package/dist/auth/clerk.js +16 -0
- package/dist/auth/cloudflare-access.js +49 -7
- package/dist/auth/downstream-oauth/consent.js +79 -29
- package/dist/auth/downstream-oauth/grant-store.js +58 -7
- package/dist/auth/downstream-oauth/refresh-records.js +6 -5
- package/dist/auth/downstream-oauth.js +12 -4
- package/dist/auth/native.js +10 -0
- package/dist/auth/oauth-request-not-sent.js +4 -0
- package/dist/auth/static-oauth.js +51 -21
- package/dist/catalog-cache.js +151 -37
- package/dist/catalog-freshness.js +13 -0
- package/dist/catalog-service.js +39 -12
- package/dist/catalog.js +103 -38
- package/dist/config.d.ts +10 -4
- package/dist/config.js +30 -7
- package/dist/connectors/remote-mcp/call.js +1 -1
- package/dist/connectors/remote-mcp/connection.js +52 -7
- package/dist/connectors/remote-mcp/oauth.js +6 -2
- package/dist/connectors/remote-mcp/resources.js +5 -2
- package/dist/connectors/remote-mcp/transport.js +37 -8
- package/dist/connectors/remote-mcp.js +8 -5
- package/dist/credential-contract.d.ts +3 -1
- package/dist/describe-config.d.ts +1 -0
- package/dist/describe-config.js +1 -0
- package/dist/downstream-input.js +11 -2
- package/dist/errors.d.ts +8 -1
- package/dist/errors.js +2 -0
- package/dist/execute.js +20 -7
- package/dist/executor-result.js +1 -1
- package/dist/executors/quickjs-child.js +1 -1
- package/dist/executors/quickjs-runtime.js +1 -1
- package/dist/executors/quickjs.js +1 -1
- package/dist/guarded-transport.js +2 -0
- package/dist/http/client.js +7 -0
- package/dist/http/hatch.js +2 -0
- package/dist/http/node-transport.js +110 -4
- package/dist/http/oauth.js +17 -6
- package/dist/index.js +10 -21
- package/dist/invocation-failure.js +28 -0
- package/dist/invocation.js +77 -35
- package/dist/membership.d.ts +1 -1
- package/dist/meta-tools.js +58 -36
- package/dist/module-contracts.d.ts +3 -1
- package/dist/native-tools.js +12 -5
- package/dist/node.d.ts +7 -0
- package/dist/node.js +82 -3
- package/dist/operation-activity.js +103 -0
- package/dist/operator-record.d.ts +8 -0
- package/dist/operator-record.js +15 -0
- package/dist/operator-ui/generated.js +2 -2
- package/dist/provider-call-facts.js +6 -2
- package/dist/provider.d.ts +1 -1
- package/dist/providers/_shared/google/workspace.d.ts +1 -1
- package/dist/providers/breeze/index.js +1 -1
- package/dist/providers/ccb/index.js +1 -1
- package/dist/providers/cloudflare/index.js +1 -1
- package/dist/providers/docs/index.js +1 -1
- package/dist/providers/drive/index.js +1 -1
- package/dist/providers/forms/index.js +1 -1
- package/dist/providers/github/index.js +1 -1
- package/dist/providers/gmail/index.js +1 -1
- package/dist/providers/granola/index.js +1 -1
- package/dist/providers/overflow/index.js +1 -1
- package/dist/providers/planning-center/index.js +1 -1
- package/dist/providers/planning-center/lib/constants.d.ts +1 -1
- package/dist/providers/quickbooks/api.js +28 -25
- package/dist/providers/quickbooks/index.js +1 -1
- package/dist/providers/quickbooks/lib/writes.js +55 -0
- package/dist/providers/quickbooks/safety.js +17 -5
- package/dist/providers/quickbooks/skill.generated.js +1 -1
- package/dist/providers/sheets/index.js +1 -1
- package/dist/providers/slides/index.js +1 -1
- package/dist/providers/stripe/index.js +2 -2
- package/dist/providers/tithely/index.js +1 -1
- package/dist/providers/vercel/api.js +1 -0
- package/dist/registry.js +16 -4
- package/dist/resource-read.js +21 -2
- package/dist/route-names.js +44 -0
- package/dist/routes/activity-context.js +1 -0
- package/dist/routes/activity-disclosure.js +26 -4
- package/dist/routes/activity.js +2 -1
- package/dist/routes/mcp.js +32 -13
- package/dist/routes/ui-config.js +1 -1
- package/dist/routes/ui-data.js +3 -2
- package/dist/runtime/native-storage.js +15 -0
- package/dist/runtime/request-reads.js +87 -10
- package/dist/runtime/request-scope.js +2 -1
- package/dist/skill-origin.js +16 -0
- package/dist/skills.js +132 -43
- package/dist/storage/d1-driver.js +33 -5
- package/dist/storage/keys.js +7 -1
- package/dist/storage/sql-native.js +85 -3
- package/dist/storage/sql.js +6 -0
- package/dist/timing-trace.js +84 -0
- package/dist/types.d.ts +5 -0
- package/dist/url-safety.js +38 -20
- package/dist/utility-recovery.js +33 -0
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/dist/worker.js +33 -8
- package/documentation/architecture.md +83 -2
- package/documentation/auth.md +132 -37
- package/documentation/code-mode.md +61 -46
- package/documentation/deploying.md +25 -1
- package/documentation/integrating.md +2 -2
- package/documentation/meta-tools.md +134 -15
- package/documentation/operating.md +86 -1
- package/documentation/operator-ui.md +51 -2
- package/documentation/providers.md +16 -14
- package/package.json +10 -8
- package/templates/node/package.json +1 -1
|
@@ -133,9 +133,13 @@ export function renewedAfterUnauthorized(request, sent, fresh) {
|
|
|
133
133
|
export function refusalRequest(error) {
|
|
134
134
|
return error !== null && typeof error === "object" ? refusals.get(error) : undefined;
|
|
135
135
|
}
|
|
136
|
-
|
|
137
|
-
|
|
136
|
+
/** A local refusal may additionally name the physical request it prevented. */
|
|
137
|
+
export function knownProviderFailure(error, unsent) {
|
|
138
|
+
if (error !== null && typeof error === "object") {
|
|
138
139
|
knownFailures.add(error);
|
|
140
|
+
if (unsent && !unsent.dispatched)
|
|
141
|
+
refusals.set(error, unsent);
|
|
142
|
+
}
|
|
139
143
|
return error;
|
|
140
144
|
}
|
|
141
145
|
export function isKnownProviderFailure(error) {
|
package/dist/provider.d.ts
CHANGED
|
@@ -89,8 +89,8 @@ export declare const PROVIDER_COMMON: {
|
|
|
89
89
|
};
|
|
90
90
|
instructions: Field<unknown, unknown, true>;
|
|
91
91
|
title: Field<unknown, unknown, true>;
|
|
92
|
-
authScope: Field<unknown, unknown, true>;
|
|
93
92
|
purpose: Field<unknown, unknown, true>;
|
|
93
|
+
authScope: Field<unknown, unknown, true>;
|
|
94
94
|
maxResultBytes: Field<unknown, unknown, true>;
|
|
95
95
|
};
|
|
96
96
|
/** The provider's maintained, connection-independent usage guide. */
|
|
@@ -92,8 +92,8 @@ export declare const GOOGLE_WORKSPACE_OPTIONS: import("../../../config-schema.js
|
|
|
92
92
|
};
|
|
93
93
|
instructions: import("../../../config-schema.js").Field<unknown, unknown, true>;
|
|
94
94
|
title: import("../../../config-schema.js").Field<unknown, unknown, true>;
|
|
95
|
-
baseUrl: import("../../../config-schema.js").Field<unknown, unknown, true>;
|
|
96
95
|
purpose: import("../../../config-schema.js").Field<unknown, unknown, true>;
|
|
96
|
+
baseUrl: import("../../../config-schema.js").Field<unknown, unknown, true>;
|
|
97
97
|
maxResultBytes: import("../../../config-schema.js").Field<unknown, unknown, true>;
|
|
98
98
|
readOnly: import("../../../config-schema.js").Field<unknown, unknown, true>;
|
|
99
99
|
subject: import("../../../config-schema.js").Field<unknown, unknown, true>;
|
|
@@ -75,7 +75,7 @@ export const breeze = defineApiProvider({
|
|
|
75
75
|
? [`Paged lists return ${options.defaultPageSize} items by default on this connection.`]
|
|
76
76
|
: []),
|
|
77
77
|
],
|
|
78
|
-
summary: "
|
|
78
|
+
summary: "Load for Breeze people, giving records, paging and guarded updates.",
|
|
79
79
|
required: true,
|
|
80
80
|
}),
|
|
81
81
|
hatch: {
|
|
@@ -81,7 +81,7 @@ export const ccb = defineApiProvider({
|
|
|
81
81
|
? [`Named lists default to ${options.defaultPerPage} rows on this connection.`]
|
|
82
82
|
: []),
|
|
83
83
|
],
|
|
84
|
-
summary:
|
|
84
|
+
summary: "Load for CCB person ids, sensitive data, paging, rate limits and guarded writes.",
|
|
85
85
|
required: true,
|
|
86
86
|
}),
|
|
87
87
|
pageSize: { default: 25, max: 100 },
|
|
@@ -75,7 +75,7 @@ function usageGuide(options) {
|
|
|
75
75
|
const { auth, accountId, zoneId, pin } = settings(options);
|
|
76
76
|
return {
|
|
77
77
|
heading: "Cloudflare usage",
|
|
78
|
-
summary: "
|
|
78
|
+
summary: "Load for Cloudflare DNS, Workers, R2, KV and analytics; resolve account and zone ids before calls.",
|
|
79
79
|
context: [
|
|
80
80
|
`Connector purpose: ${options.purpose}`,
|
|
81
81
|
`This connection already has an operator-configured ${auth === "apiToken" ? "scoped API token" : "Global API Key"}; there is no sign-in step.`,
|
|
@@ -101,7 +101,7 @@ export const docs = defineApiProvider({
|
|
|
101
101
|
"Workspace delegation is configured by the deployment. Each call acts as the mapped Workspace user; document access follows that user's Drive sharing.",
|
|
102
102
|
...(options.readOnly ? ["This connection is read-only."] : []),
|
|
103
103
|
],
|
|
104
|
-
summary: "
|
|
104
|
+
summary: "Load for Docs tabs, UTF-16 edit indexes, revision checks and partial-create recovery.",
|
|
105
105
|
required: true,
|
|
106
106
|
}),
|
|
107
107
|
hatch: {
|
|
@@ -105,7 +105,7 @@ export const drive = defineApiProvider({
|
|
|
105
105
|
`Connection purpose: ${options.purpose}`,
|
|
106
106
|
"Acts as the Workspace user deployment config maps from the admitted caller or pool owner. No caller argument selects an account. An unmapped or ownerless caller has no access unless the deployment fixes a shared account.",
|
|
107
107
|
],
|
|
108
|
-
summary: "
|
|
108
|
+
summary: "Load for Drive file discovery, bounded content, recoverable trash and sharing.",
|
|
109
109
|
required: true,
|
|
110
110
|
}),
|
|
111
111
|
tools: driveTools,
|
|
@@ -101,7 +101,7 @@ export const forms = defineApiProvider({
|
|
|
101
101
|
...(options.readOnly ? ["This connection is read-only."] : []),
|
|
102
102
|
],
|
|
103
103
|
instructions: options.instructions,
|
|
104
|
-
summary: "
|
|
104
|
+
summary: "Load for form questions, labeled responses and revision-checked batch edits.",
|
|
105
105
|
// Required: whose forms they are, that Drive lists them, and the
|
|
106
106
|
// revision-checked edit sequence are conventions no schema can carry.
|
|
107
107
|
required: true,
|
|
@@ -39,7 +39,7 @@ export const github = defineProvider({
|
|
|
39
39
|
"The App identity is already configured. Installation access is resolved on use; ask the operator to repair configuration only after an auth failure.",
|
|
40
40
|
...(options.readOnly === true ? ["This connection is read-only; every write is refused."] : []),
|
|
41
41
|
],
|
|
42
|
-
summary: "GitHub
|
|
42
|
+
summary: "Load for GitHub repository searches, releases and guarded merges.",
|
|
43
43
|
required: true,
|
|
44
44
|
});
|
|
45
45
|
const credential = options.app.privateKey === undefined
|
|
@@ -133,7 +133,7 @@ export const gmail = defineApiProvider({
|
|
|
133
133
|
"A missing subject fails auth_required; ask the operator to repair configuration after an auth failure.",
|
|
134
134
|
...(options.readOnly ? ["This connection is read-only; draft writes are refused."] : []),
|
|
135
135
|
],
|
|
136
|
-
summary: "
|
|
136
|
+
summary: "Load for Gmail search, capped message bodies, threaded replies and drafts that never send.",
|
|
137
137
|
required: true,
|
|
138
138
|
}),
|
|
139
139
|
tools: gmailTools,
|
|
@@ -46,7 +46,7 @@ export const granola = defineApiProvider({
|
|
|
46
46
|
probe,
|
|
47
47
|
guide: (options) => ({
|
|
48
48
|
heading: "Granola usage",
|
|
49
|
-
summary: "
|
|
49
|
+
summary: "Load for workspace notes and transcripts; discover accessible spaces and folders before filtering.",
|
|
50
50
|
context: [
|
|
51
51
|
`Workspace purpose: ${options.purpose}`,
|
|
52
52
|
"One shared workspace API key over REST. This connection exposes only reviewed reads.",
|
|
@@ -87,7 +87,7 @@ export const overflow = defineApiProvider({
|
|
|
87
87
|
? [`Paged lists return ${options.defaultPageSize} items by default on this connection.`]
|
|
88
88
|
: []),
|
|
89
89
|
],
|
|
90
|
-
summary: "
|
|
90
|
+
summary: "Load for giving statuses, money units, donor joins and deposit reconciliation.",
|
|
91
91
|
required: true,
|
|
92
92
|
}),
|
|
93
93
|
hatch: {
|
|
@@ -44,7 +44,7 @@ export const planningCenter = defineApiProvider({
|
|
|
44
44
|
},
|
|
45
45
|
guide: (options) => ({
|
|
46
46
|
heading: "Planning Center usage",
|
|
47
|
-
summary: "
|
|
47
|
+
summary: "Load when joining Planning Center products by person id or interpreting dates, units, roles and statuses.",
|
|
48
48
|
required: true,
|
|
49
49
|
context: [
|
|
50
50
|
`Purpose: ${options.purpose.trim()}`,
|
|
@@ -55,4 +55,4 @@ export declare const PLANNING_CENTER_MAX_RESPONSE_BYTES: number;
|
|
|
55
55
|
*/
|
|
56
56
|
export declare const PLANNING_CENTER_ADMISSION: ConnectorCallAdmissionPolicy;
|
|
57
57
|
/** Planning Center refuses a request without a descriptive one (HTTP 403). */
|
|
58
|
-
export declare const DEFAULT_USER_AGENT = "connecta/0.
|
|
58
|
+
export declare const DEFAULT_USER_AGENT = "connecta/0.41.0 (+https://github.com/zackbart/connecta)";
|
|
@@ -58,32 +58,35 @@ export const customerWrite = post("writeCustomer", "/customer", {
|
|
|
58
58
|
}).annotate(Summary, "Create a customer or sparsely update naming fields through named tools only");
|
|
59
59
|
export const invoiceWrite = post("createInvoice", "/invoice", {
|
|
60
60
|
query: { minorversion: Schema.Literal("75"), requestid: Schema.String },
|
|
61
|
-
payload: Schema.
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
61
|
+
payload: Schema.Union([
|
|
62
|
+
Schema.Struct({
|
|
63
|
+
CustomerRef: Schema.Struct({ value: PublicId }),
|
|
64
|
+
CurrencyRef: Schema.Struct({ value: Schema.String }),
|
|
65
|
+
ExchangeRate: Schema.Number,
|
|
66
|
+
TxnDate: Schema.String,
|
|
67
|
+
DueDate: Schema.String,
|
|
68
|
+
DocNumber: Schema.String,
|
|
69
|
+
Line: Schema.Array(Schema.Struct({
|
|
70
|
+
Amount: Schema.Number,
|
|
71
|
+
DetailType: Schema.Literal("SalesItemLineDetail"),
|
|
72
|
+
SalesItemLineDetail: Schema.Struct({
|
|
73
|
+
ItemRef: Schema.Struct({ value: PublicId }),
|
|
74
|
+
Qty: Schema.Literal(1),
|
|
75
|
+
UnitPrice: Schema.Number,
|
|
76
|
+
}),
|
|
77
|
+
})),
|
|
78
|
+
EmailStatus: Schema.Literal("NotSet"),
|
|
79
|
+
AllowIPNPayment: Schema.Literal(false),
|
|
80
|
+
AllowOnlinePayment: Schema.Literal(false),
|
|
81
|
+
AllowOnlineCreditCardPayment: Schema.Literal(false),
|
|
82
|
+
AllowOnlineACHPayment: Schema.Literal(false),
|
|
83
|
+
AllowOnlinePayPalPayment: Schema.Literal(false),
|
|
84
|
+
AllowOnlineAffirmPayment: Schema.Literal(false),
|
|
85
|
+
}),
|
|
86
|
+
Schema.Struct({ Id: PublicId, SyncToken, sparse: Schema.Literal(true), DocNumber: Schema.String }),
|
|
87
|
+
]),
|
|
85
88
|
success: Schema.Struct({ Invoice: RecordShape }),
|
|
86
|
-
}).annotate(Summary, "Create
|
|
89
|
+
}).annotate(Summary, "Create an unsent service invoice or sparsely update an eligible unsent invoice number through named tools only");
|
|
87
90
|
export const QuickBooksApi = vendor("QuickBooks Online", companyInfo, queryRecords, customerWrite, invoiceWrite, ...Object.values(recordReads), ...Object.values(reportReads));
|
|
88
91
|
export const QUICKBOOKS_INDEX = vendorIndex(QuickBooksApi, {
|
|
89
92
|
prefix: "quickbooks",
|
|
@@ -62,7 +62,7 @@ export const quickbooks = defineApiProvider({
|
|
|
62
62
|
: "This connection is read-only. Set readOnly: false in deployment code to enable bounded named writes.",
|
|
63
63
|
"Contact details, bank/card/tax identifiers, credentials and private links are withheld on every read.",
|
|
64
64
|
],
|
|
65
|
-
summary: "QuickBooks
|
|
65
|
+
summary: "Load for QuickBooks accounting reads and enabled customer or service-invoice writes.",
|
|
66
66
|
required: true,
|
|
67
67
|
}),
|
|
68
68
|
hatch: {
|
|
@@ -85,6 +85,61 @@ export function quickbooksWrites(connection, realmId) {
|
|
|
85
85
|
.send(customerWrite, { query: { ...version, requestid: requestId }, payload: { ...body, sparse: true } })
|
|
86
86
|
.pipe(Effect.map(({ data }) => namedRecord(object(data)["Customer"]))),
|
|
87
87
|
}),
|
|
88
|
+
write(connection, {
|
|
89
|
+
name: "update_unsent_invoice",
|
|
90
|
+
destructive: true,
|
|
91
|
+
idempotent: false,
|
|
92
|
+
description: "Sparsely change only DocNumber on an observed unsent US-company USD invoice. Requires caller-observed Id and SyncToken. Preflight refuses unknown, send-ready, delivered or payment-enabled state and stale versions. No delivery/payment settings are changed. Reconcile unknown outcomes by reading the Id; never replay.",
|
|
93
|
+
input: Schema.Struct({
|
|
94
|
+
requestId: RequestId,
|
|
95
|
+
Id: PublicId,
|
|
96
|
+
SyncToken,
|
|
97
|
+
docNumber: Schema.String.check(matches("^[A-Za-z0-9][A-Za-z0-9_-]{0,20}$")),
|
|
98
|
+
}),
|
|
99
|
+
check: (args, problems) => {
|
|
100
|
+
if (args.docNumber === "AUTO_GENERATE")
|
|
101
|
+
problems.add("docNumber", "an explicit document number other than AUTO_GENERATE");
|
|
102
|
+
},
|
|
103
|
+
output: NamedRecordShape,
|
|
104
|
+
writeSteps: { company: "once", invoice: "once", write: "once" },
|
|
105
|
+
run: (args) => Effect.gen(function* () {
|
|
106
|
+
const company = object(object((yield* connection.send(companyInfo, { params: { id: realmId }, query: version }, { step: "company", effect: "read" })).data)["CompanyInfo"]);
|
|
107
|
+
if (company["Id"] !== realmId || company["Country"] !== "US")
|
|
108
|
+
return yield* refuse("Invoice number updates require a verified US company.");
|
|
109
|
+
yield* charge;
|
|
110
|
+
const invoice = object(object((yield* connection.send(recordReads["invoices"], { params: { id: args.Id }, query: version }, { step: "invoice", effect: "read" })).data)["Invoice"]);
|
|
111
|
+
if (invoice["Id"] !== args.Id || invoice["SyncToken"] !== args.SyncToken)
|
|
112
|
+
return yield* refuse("Invoice version changed; read and review the current invoice before a new intent.");
|
|
113
|
+
if (invoice["EmailStatus"] !== "NotSet" ||
|
|
114
|
+
invoice["DeliveryInfo"] !== undefined ||
|
|
115
|
+
object(invoice["CurrencyRef"])["value"] !== "USD" ||
|
|
116
|
+
![
|
|
117
|
+
"AllowIPNPayment",
|
|
118
|
+
"AllowOnlinePayment",
|
|
119
|
+
"AllowOnlineCreditCardPayment",
|
|
120
|
+
"AllowOnlineACHPayment",
|
|
121
|
+
"AllowOnlinePayPalPayment",
|
|
122
|
+
"AllowOnlineAffirmPayment",
|
|
123
|
+
].every((key) => invoice[key] === false))
|
|
124
|
+
return yield* refuse("Only observed unsent USD invoices with explicit NotSet, no delivery information and all payment flags false qualify. Unknown state is refused.");
|
|
125
|
+
yield* charge;
|
|
126
|
+
const { data, status } = yield* connection.send(invoiceWrite, {
|
|
127
|
+
query: { ...version, requestid: args.requestId },
|
|
128
|
+
payload: { Id: args.Id, SyncToken: args.SyncToken, sparse: true, DocNumber: args.docNumber },
|
|
129
|
+
});
|
|
130
|
+
const updated = object(object(data)["Invoice"]);
|
|
131
|
+
if (updated["Id"] !== args.Id ||
|
|
132
|
+
updated["DocNumber"] !== args.docNumber ||
|
|
133
|
+
updated["SyncToken"] === args.SyncToken)
|
|
134
|
+
return yield* Effect.fail(new StatusFailure({
|
|
135
|
+
status,
|
|
136
|
+
headers: new Headers(),
|
|
137
|
+
body: undefined,
|
|
138
|
+
operation: { method: "POST", template: "/invoice" },
|
|
139
|
+
}));
|
|
140
|
+
return namedRecord(updated);
|
|
141
|
+
}),
|
|
142
|
+
}),
|
|
88
143
|
write(connection, {
|
|
89
144
|
name: "create_invoice",
|
|
90
145
|
destructive: true,
|
|
@@ -3,6 +3,13 @@ import { ConnectorCallError } from "../../errors.js";
|
|
|
3
3
|
import { withholdEmbeddedUrls } from "../_shared/rest/value-safety.js";
|
|
4
4
|
import { QUICKBOOKS_INDEX } from "./api.js";
|
|
5
5
|
const scalars = [
|
|
6
|
+
"EmailStatus",
|
|
7
|
+
"AllowIPNPayment",
|
|
8
|
+
"AllowOnlinePayment",
|
|
9
|
+
"AllowOnlineCreditCardPayment",
|
|
10
|
+
"AllowOnlineACHPayment",
|
|
11
|
+
"AllowOnlinePayPalPayment",
|
|
12
|
+
"AllowOnlineAffirmPayment",
|
|
6
13
|
"Id",
|
|
7
14
|
"SyncToken",
|
|
8
15
|
"DisplayName",
|
|
@@ -72,6 +79,7 @@ export const RecordShape = Schema.Struct({
|
|
|
72
79
|
...optional(scalars),
|
|
73
80
|
...optional(references),
|
|
74
81
|
...optional(details),
|
|
82
|
+
DeliveryInfo: Schema.optionalKey(Schema.Unknown),
|
|
75
83
|
MetaData: Schema.optionalKey(Schema.Unknown),
|
|
76
84
|
Line: Schema.optionalKey(Schema.Unknown),
|
|
77
85
|
LinkedTxn: Schema.optionalKey(Schema.Unknown),
|
|
@@ -128,6 +136,8 @@ function safeRecord(value) {
|
|
|
128
136
|
for (const key of references)
|
|
129
137
|
if (source[key])
|
|
130
138
|
out[key] = pick(source[key], ["value", "name", "type"]);
|
|
139
|
+
if (source["DeliveryInfo"] !== undefined)
|
|
140
|
+
out["DeliveryInfo"] = pick(source["DeliveryInfo"], ["DeliveryType", "DeliveryTime"]);
|
|
131
141
|
if (source["MetaData"])
|
|
132
142
|
out["MetaData"] = pick(source["MetaData"], ["CreateTime", "LastUpdatedTime"]);
|
|
133
143
|
for (const key of details)
|
|
@@ -197,11 +207,13 @@ function recordPath(path) {
|
|
|
197
207
|
return recordPath(rest);
|
|
198
208
|
const picked = head === "MetaData"
|
|
199
209
|
? ["CreateTime", "LastUpdatedTime"]
|
|
200
|
-
: head === "
|
|
201
|
-
? ["
|
|
202
|
-
:
|
|
203
|
-
? ["
|
|
204
|
-
:
|
|
210
|
+
: head === "DeliveryInfo"
|
|
211
|
+
? ["DeliveryType", "DeliveryTime"]
|
|
212
|
+
: head === "LinkedTxn"
|
|
213
|
+
? ["TxnId", "TxnType", "TxnLineId"]
|
|
214
|
+
: references.includes(head)
|
|
215
|
+
? ["value", "name", "type"]
|
|
216
|
+
: undefined;
|
|
205
217
|
if (!picked)
|
|
206
218
|
return "dropped";
|
|
207
219
|
if (rest.length === 0)
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Generated by scripts/generate-providers.mjs; do not edit.
|
|
2
2
|
export const skill = {
|
|
3
|
-
"content": "## Accounting identity and joins\n\nAn Id identifies a record inside this company and record family. DocNumber is\na human label, may repeat, and is not an Id. SyncToken is the public record\nversion used for concurrency control, not an OAuth credential. References carry\nthe target Id in value; invoice CustomerRef joins Customer, bill VendorRef\njoins Vendor, and payment Line.LinkedTxn joins invoices or bills.\n\nAccounting Payment and BillPayment records describe received and paid money;\nreading them does not execute a payment. Desktop, Payments, Payroll, attachments,\nand unreviewed accounting entities are outside this connection.\n\n## Money, time and reports\n\nAmounts use decimal currency units, never integer cents. Preserve CurrencyRef\nand ExchangeRate when combining transactions; never sum unlike currencies.\nTxnDate is the company's accounting date, not a UTC instant. MetaData timestamps\ncarry their own offset. Customer and vendor searches omit inactive records\nunless the Active filter explicitly selects them.\n\nReports are trees with Header, Columns, nested Rows and section summaries.\nRead column types and metadata together with row values; column order and\nsection labels can change. Empty amount strings mean no value, not zero.\nDo not flatten away section identity or add totals to their component rows.\nCheck the returned period, basis, currency and NoReportData option before\ncomparing reports. Aging is an as-of snapshot, rather than a transaction range.\n\nReturn aggregates and the few identifying records the task needs; avoid\ncopying full customer or vendor directories into the conversation.\n\n\n## Named writes and reconciliation\n\nThe connector defaults to read-only. Deployment code must set `readOnly: false`\nto expose named writes; endpoint and pool read-only policies still apply. The\nAPI hatch remains read-only. `create_customer` accepts DisplayName and optional\nCompanyName. `update_customer` requires caller-observed Id and SyncToken and\nchanges only those naming fields with sparse:true. Omitted CompanyName stays\nunchanged. A stale-version conflict never fetches a newer version or overwrites\nit. Read and review the current record before proposing a new update.\nDisplayName must be unique across customers, vendors and employees.\n\n`create_invoice` creates a real accounting transaction, never a draft, for a\nverified US company and a customer using USD. This bounds the no-email claim\nto the reviewed US autosend contract and money to two-decimal USD units. First\nobserve an active customer with its CurrencyRef and active Service item Ids.\nSupply currency USD, a positive explicit exchange rate, accounting and\ndue dates, a reconciliation DocNumber and 1–20 lines. Each line is one service\nunit with an explicit positive amount in decimal currency units, supplied as a\nstring with exactly two fractional digits. No prices, dates or exchange rates\nare defaulted or fetched. Preflight reads verify the fixed company Country is US\nand recheck customer currency and item activity/type; they are not an atomic snapshot. Vendor validation can still\nreject changed references. Taxes and totals are computed by QuickBooks: inspect\nthe returned currency, exchange rate and totals; line sums are not a promise of\nan untaxed total. Locale/company tax requirements may reject this narrow body.\nNo customer/item creation happens implicitly.\nEach additional request charges the call-admission budget before dispatch.\nContradictory returned references, dates, rate or service prices produce an\nunknown outcome; reconcile through reads rather than repeating the write.\n\nInvoice email status is fixed to NotSet; online, IPN, credit-card, ACH, PayPal\nand Affirm payment flags are fixed false. No email field or send operation is\nexposed. Company invoice-autosend opt-in does not supply the documented\nNeedToSend request state. This prevents this creation request from asking Intuit\nto email the invoice, including when the customer has an email on file.\
|
|
3
|
+
"content": "## Accounting identity and joins\n\nAn Id identifies a record inside this company and record family. DocNumber is\na human label, may repeat, and is not an Id. SyncToken is the public record\nversion used for concurrency control, not an OAuth credential. References carry\nthe target Id in value; invoice CustomerRef joins Customer, bill VendorRef\njoins Vendor, and payment Line.LinkedTxn joins invoices or bills.\n\nAccounting Payment and BillPayment records describe received and paid money;\nreading them does not execute a payment. Desktop, Payments, Payroll, attachments,\nand unreviewed accounting entities are outside this connection.\n\n## Money, time and reports\n\nAmounts use decimal currency units, never integer cents. Preserve CurrencyRef\nand ExchangeRate when combining transactions; never sum unlike currencies.\nTxnDate is the company's accounting date, not a UTC instant. MetaData timestamps\ncarry their own offset. Customer and vendor searches omit inactive records\nunless the Active filter explicitly selects them.\n\nReports are trees with Header, Columns, nested Rows and section summaries.\nRead column types and metadata together with row values; column order and\nsection labels can change. Empty amount strings mean no value, not zero.\nDo not flatten away section identity or add totals to their component rows.\nCheck the returned period, basis, currency and NoReportData option before\ncomparing reports. Aging is an as-of snapshot, rather than a transaction range.\n\nReturn aggregates and the few identifying records the task needs; avoid\ncopying full customer or vendor directories into the conversation.\n\n\n## Named writes and reconciliation\n\nThe connector defaults to read-only. Deployment code must set `readOnly: false`\nto expose named writes; endpoint and pool read-only policies still apply. The\nAPI hatch remains read-only. `create_customer` accepts DisplayName and optional\nCompanyName. `update_customer` requires caller-observed Id and SyncToken and\nchanges only those naming fields with sparse:true. Omitted CompanyName stays\nunchanged. A stale-version conflict never fetches a newer version or overwrites\nit. Read and review the current record before proposing a new update.\nDisplayName must be unique across customers, vendors and employees.\n\n`create_invoice` creates a real accounting transaction, never a draft, for a\nverified US company and a customer using USD. This bounds the no-email claim\nto the reviewed US autosend contract and money to two-decimal USD units. First\nobserve an active customer with its CurrencyRef and active Service item Ids.\nSupply currency USD, a positive explicit exchange rate, accounting and\ndue dates, a reconciliation DocNumber and 1–20 lines. Each line is one service\nunit with an explicit positive amount in decimal currency units, supplied as a\nstring with exactly two fractional digits. No prices, dates or exchange rates\nare defaulted or fetched. Preflight reads verify the fixed company Country is US\nand recheck customer currency and item activity/type; they are not an atomic snapshot. Vendor validation can still\nreject changed references. Taxes and totals are computed by QuickBooks: inspect\nthe returned currency, exchange rate and totals; line sums are not a promise of\nan untaxed total. Locale/company tax requirements may reject this narrow body.\nNo customer/item creation happens implicitly.\nEach additional request charges the call-admission budget before dispatch.\nContradictory returned references, dates, rate or service prices produce an\nunknown outcome; reconcile through reads rather than repeating the write.\n\nInvoice email status is fixed to NotSet; online, IPN, credit-card, ACH, PayPal\nand Affirm payment flags are fixed false. No email field or send operation is\nexposed. Company invoice-autosend opt-in does not supply the documented\nNeedToSend request state. This prevents this creation request from asking Intuit\nto email the invoice, including when the customer has an email on file.\n`update_unsent_invoice` changes only DocNumber, with caller-observed Id and\nSyncToken. It reads the US company and invoice before dispatch and requires USD,\nexplicit NotSet, no DeliveryInfo and every payment flag explicitly false.\nMissing state, send-ready and delivered invoices are refused. It preserves all\nomitted settings through sparse:true and never substitutes a newer version.\nNo date edits are exposed. This removes documented autosend prerequisites;\napplying them to updates is an inference, not an Intuit update-specific no-email\nguarantee. NotSet cannot prove that no historical delivery ever occurred.\nRead the known Id after an uncertain response; never blindly resend.\nDeletes, voids, email delivery, charges, refunds, other accounting writes and\narbitrary mutations are unavailable.\n\nEvery write requires a caller-generated `requestId`: 1–50 ASCII letters,\ndigits, underscores or hyphens, starting with a letter or digit. Keep it stable\nfor the exact intent and unique across requests/tools for this company. It is\nsent in Intuit's requestid query parameter with minorversion 75, rather than an\nidempotency header. Intuit documents recognizing the same content and ID and\nreturning the original response; no retention duration is established. This\nnever permits automatic replay. A dispatched write is sent once even after\n401, timeout, server failure or an invalid success response.\n\nAfter an unknown customer-create outcome, search exact DisplayName, including\ninactive records when needed, and inspect the match. For an invoice, search\nDocNumber plus CustomerRef and inspect currency, dates, lines and computed\namounts. DocNumber can repeat, so it alone proves nothing. Reconcile and report\nambiguity before considering another create; never generate a fresh requestId\nand blindly repeat a potentially committed intent. Unknown update outcomes\nneed a read of the known Id and version. A requestId is not a lookup endpoint or\na permanent duplicate-prevention guarantee.\n\n## Conventions\n\n- **Paging:** while `page.hasMore` is true, continue with `page.nextCursor` unchanged. For `quickbooks_api_details`, `quickbooks_api_search`, `search_records`, pass it as the top-level `cursor`; omit raw paging anchors on continuations. Keep the original method, path, filters, page size, fields and headers. Paged `list_*` and `search_*` reads take `limit` (default 25, at most 100). The last page has no `nextCursor`. If `page.hasMore` is true without a cursor, the data is incomplete and continuation was withheld. When approaching execute_code's per-program call limit, return page.nextCursor before the call that would exceed it and continue in the next program; don't restart.\n- **Results:** `fields` narrows a read's result to the dot paths you name. `truncated` lists the dot paths a result cut for size; the ids that fetch what was cut stay in the result.\n- **Hatch:** `quickbooks_api_search` finds this API's operations and reads no account data, `quickbooks_api_details` shows one operation's contract, and `quickbooks_api_read` calls a reviewed read. Details' `response.fields` promises selection only; `withheld` hints are incomplete, and absence never means safe or readable. An operation without a reviewed verdict is refused. 3 operations are refused by design; use `quickbooks_api_details` with the method and path to see each summary and refusal, even for methods outside the read/write schemas.\n- **Writes:** a write is sent once and never retried. When its outcome is unknown, read the target before repeating it. Writes take no idempotency key.\n- **Errors:** codes come from the vendor's statuses and error facts, never its prose. An unknown id, or one these credentials cannot reach, fails as `connector_call_failed`; this API does not tell the two apart. `conflict` means: Intuit fault 5010 for a stale SyncToken; Intuit fault 6240 for a duplicate DisplayName.\n- **Withheld values:** a value withheld for safety reads `\"[redacted]\"`. Writes refuse whole marker values and withheld links; longer prose may mention the marker. Paths and queries refuse any containment.",
|
|
4
4
|
"instructionsHeading": "Company instructions"
|
|
5
5
|
};
|
|
@@ -110,7 +110,7 @@ export const sheets = defineApiProvider({
|
|
|
110
110
|
...(options.readOnly ? ["This connection is read-only."] : []),
|
|
111
111
|
],
|
|
112
112
|
instructions: options.instructions,
|
|
113
|
-
summary: "
|
|
113
|
+
summary: "Load for spreadsheet A1 ranges, paged cells, RAW/USER_ENTERED writes and formatting.",
|
|
114
114
|
// Whose spreadsheets they are, how Drive finds them, and the cell-write
|
|
115
115
|
// meanings of null and "" are conventions no schema carries.
|
|
116
116
|
required: true,
|
|
@@ -130,7 +130,7 @@ export const slides = defineApiProvider({
|
|
|
130
130
|
"Generic thumbnails/preview links are refused; get_slide_thumbnail is the reviewed short-lived-link workflow. The generic batch retains only the 49 shipped request kinds; no extra preview methods are admitted.",
|
|
131
131
|
...(options.readOnly ? ["This connection is read-only; writes are refused."] : []),
|
|
132
132
|
],
|
|
133
|
-
summary: "
|
|
133
|
+
summary: "Load for deck text, notes, layouts, comments and guarded Slides edits.",
|
|
134
134
|
required: true,
|
|
135
135
|
}),
|
|
136
136
|
tools: slidesTools,
|
|
@@ -53,8 +53,8 @@ function usageGuide(options) {
|
|
|
53
53
|
return {
|
|
54
54
|
heading: "Stripe usage",
|
|
55
55
|
summary: options.mode === "production"
|
|
56
|
-
? "
|
|
57
|
-
: "
|
|
56
|
+
? "Load for live Stripe payments, refunds and invoices; real money, resolve ids before acting."
|
|
57
|
+
: "Load for sandbox Stripe payments, refunds and invoices; test data, resolve ids before acting.",
|
|
58
58
|
context: [
|
|
59
59
|
`Mode: ${options.mode}. Connector purpose: ${options.purpose}`,
|
|
60
60
|
copy.warning,
|
|
@@ -73,7 +73,7 @@ function usageGuide(options) {
|
|
|
73
73
|
? [`List tools return ${options.defaultPageSize} rows by default on this connection.`]
|
|
74
74
|
: []),
|
|
75
75
|
],
|
|
76
|
-
summary:
|
|
76
|
+
summary: "Load for giving records, donor joins, cents, ISO dates and guarded writes.",
|
|
77
77
|
// Required: the reduction rule for donor data and the id joins live only
|
|
78
78
|
// in the guide. No schema can carry either.
|
|
79
79
|
required: true,
|
|
@@ -414,6 +414,7 @@ export function usageGuide(options) {
|
|
|
414
414
|
options = pinOptions(options);
|
|
415
415
|
const team = defaultTeam(options);
|
|
416
416
|
return {
|
|
417
|
+
summary: "Load for Vercel team scope, project/deployment ids, build events, runtime logs and guarded writes.",
|
|
417
418
|
context: [
|
|
418
419
|
`Vercel REST access token is already configured. Account purpose: ${options.purpose}`,
|
|
419
420
|
options.pin
|
package/dist/registry.js
CHANGED
|
@@ -20,7 +20,7 @@ import { attachOAuthSealer, vaultOAuthSealer } from "./oauth-sealing.js";
|
|
|
20
20
|
import { boundedStatus, failureRecord, failureStatus, logFailure, ownStatus } from "./operator-record.js";
|
|
21
21
|
import { ObservedOutputSchemas } from "./result-shapes.js";
|
|
22
22
|
import { ContinuationBinding, OperationCredentials, ContinuationStorage, operationView, setViewService, duringViewEffect, } from "./runtime/call.js";
|
|
23
|
-
import { hasNativeStorage, registerNativeStorage } from "./runtime/native-storage.js";
|
|
23
|
+
import { getNativeStorage, hasNativeStorage, registerNativeStorage } from "./runtime/native-storage.js";
|
|
24
24
|
import { observeStorageWrites } from "./runtime/request-reads.js";
|
|
25
25
|
import { attachStorageDigestKey } from "./storage-digest.js";
|
|
26
26
|
import { requestReads } from "./runtime/request-scope.js";
|
|
@@ -31,7 +31,7 @@ import { Storage } from "./runtime/services.js";
|
|
|
31
31
|
import { SharedRead } from "./runtime/shared-read.js";
|
|
32
32
|
import { runOnPartition, storageCompareAndSet, storageDelete, storageGet, storageGetMany, storageSet, storageList, } from "./runtime/storage.js";
|
|
33
33
|
import { redactCatalog, sentSecretsFor, sentSecretsForRequest } from "./sent-secrets.js";
|
|
34
|
-
import { GUIDE_SUMMARY_LENGTH, normalizeGuideSummary } from "./skills.js";
|
|
34
|
+
import { GUIDE_SUMMARY_LENGTH, normalizeGuideSummary, connectorGuide, connectorGuideSummary } from "./skills.js";
|
|
35
35
|
import { jsonCodec, OAUTH_HANDOFF_TTL_SECONDS, oauthHandoffKeys, resultKeys, scopes, stashExpiryKeys, stashLedgerKeys, } from "./storage/keys.js";
|
|
36
36
|
import { assertStaticToolNames, hasControlCharacters } from "./tool-name.js";
|
|
37
37
|
import { carryToolClassification, classifyTool } from "./tool-safety.js";
|
|
@@ -298,6 +298,13 @@ export class Registry {
|
|
|
298
298
|
`normalization; the discovery bound is ${GUIDE_SUMMARY_LENGTH}. ` +
|
|
299
299
|
"Shorten it or omit it to derive one.");
|
|
300
300
|
}
|
|
301
|
+
// Advisory English-language heuristic: a task verb or operation noun
|
|
302
|
+
// distinguishes actionable guidance from credential/scope facts. Never
|
|
303
|
+
// log guide text, which may contain deployment-private information.
|
|
304
|
+
if (connectorGuide(c) !== undefined &&
|
|
305
|
+
!/\b(load|use|when|read|reads|write|writes|search|query|inspect|resolve|list|review|edit|create|update|deploy|paging|reconciliation|accounting)\b/i.test(connectorGuideSummary(c) ?? "")) {
|
|
306
|
+
opts.logger.warn(`Connector "${c.id}" guide needs an actionable summary; say when to load it with usageGuide.summary.`);
|
|
307
|
+
}
|
|
301
308
|
this.connectors.set(c.id, c);
|
|
302
309
|
if (c.callAdmission !== undefined) {
|
|
303
310
|
this.callAdmission.set(c.id, new ConnectorCallAdmissionController(c.id, c.callAdmission));
|
|
@@ -874,16 +881,21 @@ export class Registry {
|
|
|
874
881
|
// ID after failure or timeout, including a late header commit.
|
|
875
882
|
const remaining = Math.max(0, writeDeadline - (yield* Clock.currentTimeMillis));
|
|
876
883
|
const written = yield* Effect.gen(function* () {
|
|
877
|
-
|
|
884
|
+
const multi = getNativeStorage(self.opts.storage)?.setMany;
|
|
885
|
+
for (let index = keys.length - 1; index >= 0;) {
|
|
886
|
+
const first = multi && index > 0 ? Math.max(1, index - 63) : index;
|
|
878
887
|
const before = yield* Clock.currentTimeMillis;
|
|
879
888
|
if (before >= writeDeadline || before >= chunkExpiry)
|
|
880
889
|
return false;
|
|
881
890
|
pendingWrite = true;
|
|
882
|
-
yield*
|
|
891
|
+
yield* (multi && index > 0
|
|
892
|
+
? multi(Array.from({ length: index - first + 1 }, (_, offset) => [keys[index - offset], chunks[index - offset]]), { expiresAtMs: chunkExpiry })
|
|
893
|
+
: storageSet(keys[index], chunks[index], { expiresAtMs: chunkExpiry })).pipe(Effect.tap(() => Effect.sync(() => {
|
|
883
894
|
pendingWrite = false;
|
|
884
895
|
})));
|
|
885
896
|
if ((yield* Clock.currentTimeMillis) >= writeDeadline)
|
|
886
897
|
return false;
|
|
898
|
+
index = first - 1;
|
|
887
899
|
}
|
|
888
900
|
return true;
|
|
889
901
|
}).pipe(Effect.timeoutOrElse({ duration: Duration.millis(remaining), orElse: () => Effect.succeed(false) }), Effect.onError(() => release.pipe(Effect.ignore)));
|
package/dist/resource-read.js
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { utilityRecovery } from "./utility-recovery.js";
|
|
1
2
|
import { Effect } from "effect";
|
|
2
3
|
import { classifyCallError, ConnectorCallError } from "./errors.js";
|
|
3
4
|
import { InvocationFailure, timed } from "./invocation.js";
|
|
4
5
|
import { withDeadlineEffect } from "./runtime/run.js";
|
|
5
6
|
import { sentSecretsFor } from "./sent-secrets.js";
|
|
7
|
+
import { downstreamOrigin, isSkillResource } from "./skill-origin.js";
|
|
6
8
|
import { hasControlCharacters } from "./tool-name.js";
|
|
7
9
|
const encoder = new TextEncoder();
|
|
8
10
|
const URI_HINT = "Use resource://<connectorId>/<encodeURIComponent(downstreamUri)> with a whole-connector grant.";
|
|
@@ -26,13 +28,26 @@ function resourceTarget(registry, raw) {
|
|
|
26
28
|
const connector = registry.getResourceConnector(match[1]);
|
|
27
29
|
if (!connector?.readResource) {
|
|
28
30
|
throw new InvocationFailure({
|
|
29
|
-
|
|
31
|
+
...utilityRecovery(connector ? "unsupported_capability" : "unknown_address", "No resource reader is available."),
|
|
32
|
+
code: connector ? "unsupported_capability" : "unknown_address",
|
|
30
33
|
message: `No resource reader is available for this address. ${URI_HINT}`,
|
|
31
34
|
retryable: false,
|
|
32
35
|
});
|
|
33
36
|
}
|
|
34
37
|
return { connector, uri };
|
|
35
38
|
}
|
|
39
|
+
/** Frame skill text for a program; native resources/read keeps exact bytes and never comes here. */
|
|
40
|
+
function labelSkillText(contents, connectorId, uri) {
|
|
41
|
+
const items = contents?.contents;
|
|
42
|
+
if (!Array.isArray(items))
|
|
43
|
+
return contents;
|
|
44
|
+
return {
|
|
45
|
+
...contents,
|
|
46
|
+
contents: items.map((item) => typeof item?.text === "string" && (isSkillResource(uri) || isSkillResource(String(item.uri ?? "")))
|
|
47
|
+
? { ...item, text: `${downstreamOrigin(connectorId)}\n\n${item.text}` }
|
|
48
|
+
: item),
|
|
49
|
+
};
|
|
50
|
+
}
|
|
36
51
|
/** A protocol read, under the same request scope, call permit and deadline as tools. */
|
|
37
52
|
export function readResource(registry, raw, options) {
|
|
38
53
|
let connector;
|
|
@@ -68,7 +83,7 @@ export function readResource(registry, raw, options) {
|
|
|
68
83
|
},
|
|
69
84
|
catch: (error) => error,
|
|
70
85
|
}));
|
|
71
|
-
return options.sentSecrets.redact(contents);
|
|
86
|
+
return labelSkillText(options.sentSecrets.redact(contents), target.connector.id, target.uri);
|
|
72
87
|
})), {
|
|
73
88
|
timeoutMs: options.timeoutMs,
|
|
74
89
|
...(options.signal ? { signal: options.signal } : {}),
|
|
@@ -98,6 +113,10 @@ export function readResource(registry, raw, options) {
|
|
|
98
113
|
retry: "Retry connecta.read with the same resource URI after the operator completes recovery.",
|
|
99
114
|
};
|
|
100
115
|
}
|
|
116
|
+
if (["unavailable", "unsupported_capability", "input_required_unsupported"].includes(details.code) &&
|
|
117
|
+
!details.nextAction) {
|
|
118
|
+
details = { ...details, nextAction: utilityRecovery(details.code, details.message).nextAction };
|
|
119
|
+
}
|
|
101
120
|
return Effect.fail(new InvocationFailure(options.sentSecrets.redact(details)));
|
|
102
121
|
}));
|
|
103
122
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `meta`: the six meta-tools. `code`: in-program wording, selected only by the
|
|
3
|
+
* eval code arm. `native` and `native-code`: native pools without and with
|
|
4
|
+
* `execute_code`.
|
|
5
|
+
*/
|
|
6
|
+
const NAMES = {
|
|
7
|
+
meta: Object.freeze({
|
|
8
|
+
search: "search_tools",
|
|
9
|
+
read: "call_tool",
|
|
10
|
+
write: "call_destructive_tool",
|
|
11
|
+
program: "execute_code",
|
|
12
|
+
authorize: "authorize_connector",
|
|
13
|
+
skills: "skills",
|
|
14
|
+
}),
|
|
15
|
+
code: Object.freeze({
|
|
16
|
+
search: "connecta.search inside execute_code",
|
|
17
|
+
read: "connecta.call inside execute_code",
|
|
18
|
+
write: "connecta.call inside execute_code",
|
|
19
|
+
program: "execute_code",
|
|
20
|
+
authorize: "authorize_connector",
|
|
21
|
+
skills: "skills",
|
|
22
|
+
}),
|
|
23
|
+
native: Object.freeze({
|
|
24
|
+
search: "your host's tool search",
|
|
25
|
+
read: "the original native tool",
|
|
26
|
+
write: "the original native tool",
|
|
27
|
+
program: "your host's tool search",
|
|
28
|
+
authorize: "the returned authorization handoff",
|
|
29
|
+
skills: "skills",
|
|
30
|
+
}),
|
|
31
|
+
"native-code": Object.freeze({
|
|
32
|
+
search: "your host's tool search",
|
|
33
|
+
read: "the original native tool",
|
|
34
|
+
write: "the original native tool",
|
|
35
|
+
program: "execute_code",
|
|
36
|
+
authorize: "the returned authorization handoff",
|
|
37
|
+
skills: "skills",
|
|
38
|
+
}),
|
|
39
|
+
};
|
|
40
|
+
/** The six-tool surface's names: the default wherever no other surface applies. */
|
|
41
|
+
export const META_ROUTES = NAMES.meta;
|
|
42
|
+
export function routeNames(surface) {
|
|
43
|
+
return NAMES[surface];
|
|
44
|
+
}
|
|
@@ -8,6 +8,7 @@ export function operatorActivityContext(context, authz) {
|
|
|
8
8
|
return {
|
|
9
9
|
sink: activity.store,
|
|
10
10
|
recordTool: activity.recordTool,
|
|
11
|
+
recordOperation: activity.recordOperation,
|
|
11
12
|
actor: authz.actor,
|
|
12
13
|
...(principal
|
|
13
14
|
? { principalActor: { kind: authz.actor.kind, id: principal.id, namespace: principal.namespace } }
|