feat(discord): SetSpark record client for the Discord Sage, fixed verbs against setspark-api, connector-verified approvals (#1509)
Row 25, parts 2a and 2b, against the shared-signals contract a5425a2. Model side: eight fixed verbs in the pi extension (record_list, record_get, record_create, record_update, resolve_id, open_approval_request, get_approval_request, create_document), each one HTTP call with arguments checked before any request. Writes carry an idempotency key <principal>:<message id>:<call index> and an audit context. The seat key is read from a 0600 file on every call and never cached, printed or journaled. Connector side: append-only approval ledger, Approve button and exact "approve" reply resolved by the connector against the required approvers, confirmation message posted as button evidence, bind and add_approval through the service under connector keys, retry of unknown entries on start. Evidence: node tests 162 pass, scripts/test-discord.sh 63/63. Review by rev-code-02, round 1 approved (#1509 comment 26467, tree 7872d8c5). Co-Authored-By: Claude Fable 5.1 <[email protected]>
This commit is contained in:
@@ -20,6 +20,7 @@
|
||||
|
||||
import { existsSync, lstatSync, readFileSync, realpathSync, statSync } from "node:fs";
|
||||
import { loadWebConfig } from "./web.mjs";
|
||||
import { loadSetsparkConfig } from "./setspark.mjs";
|
||||
import { isAbsolute, join, resolve, sep } from "node:path";
|
||||
import { homedir } from "node:os";
|
||||
import { DiscordError } from "./errors.mjs";
|
||||
@@ -45,7 +46,7 @@ const USER_KEYS = ["id", "name", "channels"];
|
||||
const ENGINE_KEYS = ["provider", "model", "thinking"];
|
||||
const LIMIT_KEYS = Object.keys(LIMIT_DEFAULTS);
|
||||
const CONTEXT_KEYS = ["files"];
|
||||
const TOOLS_KEYS = ["roots", "maxFileBytes", "maxCallsPerTurn", "web"];
|
||||
const TOOLS_KEYS = ["roots", "maxFileBytes", "maxCallsPerTurn", "web", "setspark"];
|
||||
const ROOT_KEYS = ["name", "path", "write", "git"];
|
||||
const ROOT_NAME = /^[a-z0-9][a-z0-9._-]{0,63}$/;
|
||||
|
||||
@@ -216,6 +217,7 @@ export function validateBinding(raw, where = "binding") {
|
||||
maxFileBytes: requireInteger(mergedTools, "maxFileBytes", `${where}.tools`, { min: 1024, max: 4 * 1024 * 1024 }),
|
||||
maxCallsPerTurn: requireInteger(mergedTools, "maxCallsPerTurn", `${where}.tools`, { min: 1, max: 64 }),
|
||||
web: raw.tools.web === undefined ? null : webConfig(raw.tools.web, `${where}.tools.web`),
|
||||
setspark: raw.tools.setspark === undefined ? null : setsparkConfig(raw.tools.setspark, `${where}.tools.setspark`),
|
||||
});
|
||||
}
|
||||
|
||||
@@ -326,6 +328,14 @@ export function resolveContextFiles(binding, repo) {
|
||||
// Tool roots must exist as real directories on this host, not symlinks, and
|
||||
// must not sit inside the data root (bindings, tokens, journals) or contain
|
||||
// it. Returns the resolved config the engine hands the extension.
|
||||
function setsparkConfig(raw, where) {
|
||||
try {
|
||||
return loadSetsparkConfig(raw, where);
|
||||
} catch (err) {
|
||||
throw new DiscordError(err.message);
|
||||
}
|
||||
}
|
||||
|
||||
function webConfig(raw, where) {
|
||||
try {
|
||||
return loadWebConfig(raw, where);
|
||||
|
||||
Reference in New Issue
Block a user