@botiverse/raft-sdk 0.6.0 → 0.7.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/README.md +146 -0
- package/dist/cjs/index.cjs +1538 -194
- package/dist/esm/index.js +1501 -195
- package/dist/index.d.ts +1363 -61
- package/operations.json +1964 -0
- package/package.json +6 -3
package/dist/cjs/index.cjs
CHANGED
|
@@ -3113,6 +3113,12 @@ const numberProcessor = (schema, ctx, _json, _params) => {
|
|
|
3113
3113
|
const booleanProcessor = (_schema, _ctx, json, _params) => {
|
|
3114
3114
|
json.type = "boolean";
|
|
3115
3115
|
};
|
|
3116
|
+
const bigintProcessor = (_schema, ctx, _json, _params) => {
|
|
3117
|
+
if (ctx.unrepresentable === "throw") throw new Error("BigInt cannot be represented in JSON Schema");
|
|
3118
|
+
};
|
|
3119
|
+
const symbolProcessor = (_schema, ctx, _json, _params) => {
|
|
3120
|
+
if (ctx.unrepresentable === "throw") throw new Error("Symbols cannot be represented in JSON Schema");
|
|
3121
|
+
};
|
|
3116
3122
|
const nullProcessor = (_schema, ctx, json, _params) => {
|
|
3117
3123
|
if (ctx.target === "openapi-3.0") {
|
|
3118
3124
|
json.type = "string";
|
|
@@ -3120,9 +3126,20 @@ const nullProcessor = (_schema, ctx, json, _params) => {
|
|
|
3120
3126
|
json.enum = [null];
|
|
3121
3127
|
} else json.type = "null";
|
|
3122
3128
|
};
|
|
3129
|
+
const undefinedProcessor = (_schema, ctx, _json, _params) => {
|
|
3130
|
+
if (ctx.unrepresentable === "throw") throw new Error("Undefined cannot be represented in JSON Schema");
|
|
3131
|
+
};
|
|
3132
|
+
const voidProcessor = (_schema, ctx, _json, _params) => {
|
|
3133
|
+
if (ctx.unrepresentable === "throw") throw new Error("Void cannot be represented in JSON Schema");
|
|
3134
|
+
};
|
|
3123
3135
|
const neverProcessor = (_schema, _ctx, json, _params) => {
|
|
3124
3136
|
json.not = {};
|
|
3125
3137
|
};
|
|
3138
|
+
const anyProcessor = (_schema, _ctx, _json, _params) => {};
|
|
3139
|
+
const unknownProcessor = (_schema, _ctx, _json, _params) => {};
|
|
3140
|
+
const dateProcessor = (_schema, ctx, _json, _params) => {
|
|
3141
|
+
if (ctx.unrepresentable === "throw") throw new Error("Date cannot be represented in JSON Schema");
|
|
3142
|
+
};
|
|
3126
3143
|
const enumProcessor = (schema, _ctx, json, _params) => {
|
|
3127
3144
|
const def = schema._zod.def;
|
|
3128
3145
|
const values = getEnumValues(def.entries);
|
|
@@ -3152,12 +3169,54 @@ const literalProcessor = (schema, ctx, json, _params) => {
|
|
|
3152
3169
|
json.enum = vals;
|
|
3153
3170
|
}
|
|
3154
3171
|
};
|
|
3172
|
+
const nanProcessor = (_schema, ctx, _json, _params) => {
|
|
3173
|
+
if (ctx.unrepresentable === "throw") throw new Error("NaN cannot be represented in JSON Schema");
|
|
3174
|
+
};
|
|
3175
|
+
const templateLiteralProcessor = (schema, _ctx, json, _params) => {
|
|
3176
|
+
const _json = json;
|
|
3177
|
+
const pattern = schema._zod.pattern;
|
|
3178
|
+
if (!pattern) throw new Error("Pattern not found in template literal");
|
|
3179
|
+
_json.type = "string";
|
|
3180
|
+
_json.pattern = pattern.source;
|
|
3181
|
+
};
|
|
3182
|
+
const fileProcessor = (schema, _ctx, json, _params) => {
|
|
3183
|
+
const _json = json;
|
|
3184
|
+
const file = {
|
|
3185
|
+
type: "string",
|
|
3186
|
+
format: "binary",
|
|
3187
|
+
contentEncoding: "binary"
|
|
3188
|
+
};
|
|
3189
|
+
const { minimum, maximum, mime } = schema._zod.bag;
|
|
3190
|
+
if (minimum !== void 0) file.minLength = minimum;
|
|
3191
|
+
if (maximum !== void 0) file.maxLength = maximum;
|
|
3192
|
+
if (mime) {
|
|
3193
|
+
if (mime.length === 1) {
|
|
3194
|
+
file.contentMediaType = mime[0];
|
|
3195
|
+
Object.assign(_json, file);
|
|
3196
|
+
} else {
|
|
3197
|
+
Object.assign(_json, file);
|
|
3198
|
+
_json.anyOf = mime.map((m) => ({ contentMediaType: m }));
|
|
3199
|
+
}
|
|
3200
|
+
} else Object.assign(_json, file);
|
|
3201
|
+
};
|
|
3202
|
+
const successProcessor = (_schema, _ctx, json, _params) => {
|
|
3203
|
+
json.type = "boolean";
|
|
3204
|
+
};
|
|
3155
3205
|
const customProcessor = (_schema, ctx, _json, _params) => {
|
|
3156
3206
|
if (ctx.unrepresentable === "throw") throw new Error("Custom types cannot be represented in JSON Schema");
|
|
3157
3207
|
};
|
|
3208
|
+
const functionProcessor = (_schema, ctx, _json, _params) => {
|
|
3209
|
+
if (ctx.unrepresentable === "throw") throw new Error("Function types cannot be represented in JSON Schema");
|
|
3210
|
+
};
|
|
3158
3211
|
const transformProcessor = (_schema, ctx, _json, _params) => {
|
|
3159
3212
|
if (ctx.unrepresentable === "throw") throw new Error("Transforms cannot be represented in JSON Schema");
|
|
3160
3213
|
};
|
|
3214
|
+
const mapProcessor = (_schema, ctx, _json, _params) => {
|
|
3215
|
+
if (ctx.unrepresentable === "throw") throw new Error("Map cannot be represented in JSON Schema");
|
|
3216
|
+
};
|
|
3217
|
+
const setProcessor = (_schema, ctx, _json, _params) => {
|
|
3218
|
+
if (ctx.unrepresentable === "throw") throw new Error("Set cannot be represented in JSON Schema");
|
|
3219
|
+
};
|
|
3161
3220
|
const arrayProcessor = (schema, ctx, _json, params) => {
|
|
3162
3221
|
const json = _json;
|
|
3163
3222
|
const def = schema._zod.def;
|
|
@@ -3234,6 +3293,44 @@ const intersectionProcessor = (schema, ctx, json, params) => {
|
|
|
3234
3293
|
const isSimpleIntersection = (val) => "allOf" in val && Object.keys(val).length === 1;
|
|
3235
3294
|
json.allOf = [...isSimpleIntersection(a) ? a.allOf : [a], ...isSimpleIntersection(b) ? b.allOf : [b]];
|
|
3236
3295
|
};
|
|
3296
|
+
const tupleProcessor = (schema, ctx, _json, params) => {
|
|
3297
|
+
const json = _json;
|
|
3298
|
+
const def = schema._zod.def;
|
|
3299
|
+
json.type = "array";
|
|
3300
|
+
const prefixPath = ctx.target === "draft-2020-12" ? "prefixItems" : "items";
|
|
3301
|
+
const restPath = ctx.target === "draft-2020-12" ? "items" : ctx.target === "openapi-3.0" ? "items" : "additionalItems";
|
|
3302
|
+
const prefixItems = def.items.map((x, i) => process$1(x, ctx, {
|
|
3303
|
+
...params,
|
|
3304
|
+
path: [
|
|
3305
|
+
...params.path,
|
|
3306
|
+
prefixPath,
|
|
3307
|
+
i
|
|
3308
|
+
]
|
|
3309
|
+
}));
|
|
3310
|
+
const rest = def.rest ? process$1(def.rest, ctx, {
|
|
3311
|
+
...params,
|
|
3312
|
+
path: [
|
|
3313
|
+
...params.path,
|
|
3314
|
+
restPath,
|
|
3315
|
+
...ctx.target === "openapi-3.0" ? [def.items.length] : []
|
|
3316
|
+
]
|
|
3317
|
+
}) : null;
|
|
3318
|
+
if (ctx.target === "draft-2020-12") {
|
|
3319
|
+
json.prefixItems = prefixItems;
|
|
3320
|
+
if (rest) json.items = rest;
|
|
3321
|
+
} else if (ctx.target === "openapi-3.0") {
|
|
3322
|
+
json.items = { anyOf: prefixItems };
|
|
3323
|
+
if (rest) json.items.anyOf.push(rest);
|
|
3324
|
+
json.minItems = prefixItems.length;
|
|
3325
|
+
if (!rest) json.maxItems = prefixItems.length;
|
|
3326
|
+
} else {
|
|
3327
|
+
json.items = prefixItems;
|
|
3328
|
+
if (rest) json.additionalItems = rest;
|
|
3329
|
+
}
|
|
3330
|
+
const { minimum, maximum } = schema._zod.bag;
|
|
3331
|
+
if (typeof minimum === "number") json.minItems = minimum;
|
|
3332
|
+
if (typeof maximum === "number") json.maxItems = maximum;
|
|
3333
|
+
};
|
|
3237
3334
|
const recordProcessor = (schema, ctx, _json, params) => {
|
|
3238
3335
|
const json = _json;
|
|
3239
3336
|
const def = schema._zod.def;
|
|
@@ -3323,12 +3420,99 @@ const readonlyProcessor = (schema, ctx, json, params) => {
|
|
|
3323
3420
|
seen.ref = def.innerType;
|
|
3324
3421
|
json.readOnly = true;
|
|
3325
3422
|
};
|
|
3423
|
+
const promiseProcessor = (schema, ctx, _json, params) => {
|
|
3424
|
+
const def = schema._zod.def;
|
|
3425
|
+
process$1(def.innerType, ctx, params);
|
|
3426
|
+
const seen = ctx.seen.get(schema);
|
|
3427
|
+
seen.ref = def.innerType;
|
|
3428
|
+
};
|
|
3326
3429
|
const optionalProcessor = (schema, ctx, _json, params) => {
|
|
3327
3430
|
const def = schema._zod.def;
|
|
3328
3431
|
process$1(def.innerType, ctx, params);
|
|
3329
3432
|
const seen = ctx.seen.get(schema);
|
|
3330
3433
|
seen.ref = def.innerType;
|
|
3331
3434
|
};
|
|
3435
|
+
const lazyProcessor = (schema, ctx, _json, params) => {
|
|
3436
|
+
const innerType = schema._zod.innerType;
|
|
3437
|
+
process$1(innerType, ctx, params);
|
|
3438
|
+
const seen = ctx.seen.get(schema);
|
|
3439
|
+
seen.ref = innerType;
|
|
3440
|
+
};
|
|
3441
|
+
const allProcessors = {
|
|
3442
|
+
string: stringProcessor,
|
|
3443
|
+
number: numberProcessor,
|
|
3444
|
+
boolean: booleanProcessor,
|
|
3445
|
+
bigint: bigintProcessor,
|
|
3446
|
+
symbol: symbolProcessor,
|
|
3447
|
+
null: nullProcessor,
|
|
3448
|
+
undefined: undefinedProcessor,
|
|
3449
|
+
void: voidProcessor,
|
|
3450
|
+
never: neverProcessor,
|
|
3451
|
+
any: anyProcessor,
|
|
3452
|
+
unknown: unknownProcessor,
|
|
3453
|
+
date: dateProcessor,
|
|
3454
|
+
enum: enumProcessor,
|
|
3455
|
+
literal: literalProcessor,
|
|
3456
|
+
nan: nanProcessor,
|
|
3457
|
+
template_literal: templateLiteralProcessor,
|
|
3458
|
+
file: fileProcessor,
|
|
3459
|
+
success: successProcessor,
|
|
3460
|
+
custom: customProcessor,
|
|
3461
|
+
function: functionProcessor,
|
|
3462
|
+
transform: transformProcessor,
|
|
3463
|
+
map: mapProcessor,
|
|
3464
|
+
set: setProcessor,
|
|
3465
|
+
array: arrayProcessor,
|
|
3466
|
+
object: objectProcessor,
|
|
3467
|
+
union: unionProcessor,
|
|
3468
|
+
intersection: intersectionProcessor,
|
|
3469
|
+
tuple: tupleProcessor,
|
|
3470
|
+
record: recordProcessor,
|
|
3471
|
+
nullable: nullableProcessor,
|
|
3472
|
+
nonoptional: nonoptionalProcessor,
|
|
3473
|
+
default: defaultProcessor,
|
|
3474
|
+
prefault: prefaultProcessor,
|
|
3475
|
+
catch: catchProcessor,
|
|
3476
|
+
pipe: pipeProcessor,
|
|
3477
|
+
readonly: readonlyProcessor,
|
|
3478
|
+
promise: promiseProcessor,
|
|
3479
|
+
optional: optionalProcessor,
|
|
3480
|
+
lazy: lazyProcessor
|
|
3481
|
+
};
|
|
3482
|
+
function toJSONSchema(input, params) {
|
|
3483
|
+
if ("_idmap" in input) {
|
|
3484
|
+
const registry = input;
|
|
3485
|
+
const ctx = initializeContext({
|
|
3486
|
+
...params,
|
|
3487
|
+
processors: allProcessors
|
|
3488
|
+
});
|
|
3489
|
+
const defs = {};
|
|
3490
|
+
for (const entry of registry._idmap.entries()) {
|
|
3491
|
+
const [_, schema] = entry;
|
|
3492
|
+
process$1(schema, ctx);
|
|
3493
|
+
}
|
|
3494
|
+
const schemas = {};
|
|
3495
|
+
ctx.external = {
|
|
3496
|
+
registry,
|
|
3497
|
+
uri: params?.uri,
|
|
3498
|
+
defs
|
|
3499
|
+
};
|
|
3500
|
+
for (const entry of registry._idmap.entries()) {
|
|
3501
|
+
const [key, schema] = entry;
|
|
3502
|
+
extractDefs(ctx, schema);
|
|
3503
|
+
schemas[key] = finalize(ctx, schema);
|
|
3504
|
+
}
|
|
3505
|
+
if (Object.keys(defs).length > 0) schemas.__shared = { [ctx.target === "draft-2020-12" ? "$defs" : "definitions"]: defs };
|
|
3506
|
+
return { schemas };
|
|
3507
|
+
}
|
|
3508
|
+
const ctx = initializeContext({
|
|
3509
|
+
...params,
|
|
3510
|
+
processors: allProcessors
|
|
3511
|
+
});
|
|
3512
|
+
process$1(input, ctx);
|
|
3513
|
+
extractDefs(ctx, input);
|
|
3514
|
+
return finalize(ctx, input);
|
|
3515
|
+
}
|
|
3332
3516
|
//#endregion
|
|
3333
3517
|
//#region ../../node_modules/.pnpm/zod@4.3.6/node_modules/zod/v4/classic/iso.js
|
|
3334
3518
|
const ZodISODateTime = /*@__PURE__*/ $constructor("ZodISODateTime", (inst, def) => {
|
|
@@ -7622,7 +7806,7 @@ async function joinRaftChannelByTarget(client, request) {
|
|
|
7622
7806
|
//#endregion
|
|
7623
7807
|
//#region src/events.ts
|
|
7624
7808
|
const sequence = number$1().int().nonnegative().max(Number.MAX_SAFE_INTEGER);
|
|
7625
|
-
const requestSchema = object({
|
|
7809
|
+
const requestSchema$1 = object({
|
|
7626
7810
|
since: union([sequence, literal("latest")]).optional(),
|
|
7627
7811
|
limit: number$1().int().min(1).max(200).optional(),
|
|
7628
7812
|
ack: literal("cursor").optional()
|
|
@@ -7678,7 +7862,7 @@ function failure$2(code, status, cursorAck = false) {
|
|
|
7678
7862
|
}
|
|
7679
7863
|
/** Internal adapter. The caller must supply a single-attempt transport: receive drains the returned batch. */
|
|
7680
7864
|
async function receiveRaftEvents(client, request = {}) {
|
|
7681
|
-
const parsed = requestSchema.safeParse(request);
|
|
7865
|
+
const parsed = requestSchema$1.safeParse(request);
|
|
7682
7866
|
if (!parsed.success) return failure$2("INVALID_REQUEST");
|
|
7683
7867
|
const cursorAck = parsed.data.ack === "cursor";
|
|
7684
7868
|
const result = await client.events.get({
|
|
@@ -10267,7 +10451,8 @@ const DEFAULT_MESSAGES = {
|
|
|
10267
10451
|
CONFLICT: "The Raft Server refused the operation because of the current state.",
|
|
10268
10452
|
IDEMPOTENCY_KEY_REUSED: "This idempotency key was already used for a different message.",
|
|
10269
10453
|
UNSUPPORTED_FOR_EXTERNAL_AGENTS: "This operation is not available to External Agents.",
|
|
10270
|
-
UNAVAILABLE: "The Raft Server could not serve this operation right now."
|
|
10454
|
+
UNAVAILABLE: "The Raft Server could not serve this operation right now.",
|
|
10455
|
+
MODEL_ONLY: "This operation only counts when the model sees its result, so it cannot be run from code; nothing was sent."
|
|
10271
10456
|
};
|
|
10272
10457
|
const DEFAULT_NEXT_ACTION = {
|
|
10273
10458
|
INVALID_REQUEST: "Fix the request arguments; nothing was sent.",
|
|
@@ -10280,7 +10465,8 @@ const DEFAULT_NEXT_ACTION = {
|
|
|
10280
10465
|
CONFLICT: "Read the current state before repeating this operation.",
|
|
10281
10466
|
IDEMPOTENCY_KEY_REUSED: "Use a new idempotency key for a different message, or resend the identical payload to reconcile.",
|
|
10282
10467
|
UNSUPPORTED_FOR_EXTERNAL_AGENTS: "Use your own runtime for this; the Server does not provide it to External Agents.",
|
|
10283
|
-
UNAVAILABLE: "Retry in a moment."
|
|
10468
|
+
UNAVAILABLE: "Retry in a moment.",
|
|
10469
|
+
MODEL_ONLY: "Call it as a model tool call instead of from code."
|
|
10284
10470
|
};
|
|
10285
10471
|
const RETRYABLE = {
|
|
10286
10472
|
INVALID_REQUEST: false,
|
|
@@ -10293,7 +10479,8 @@ const RETRYABLE = {
|
|
|
10293
10479
|
CONFLICT: false,
|
|
10294
10480
|
IDEMPOTENCY_KEY_REUSED: false,
|
|
10295
10481
|
UNSUPPORTED_FOR_EXTERNAL_AGENTS: false,
|
|
10296
|
-
UNAVAILABLE: true
|
|
10482
|
+
UNAVAILABLE: true,
|
|
10483
|
+
MODEL_ONLY: false
|
|
10297
10484
|
};
|
|
10298
10485
|
function classifyHttp(status, serverCode) {
|
|
10299
10486
|
const code = serverCode?.toLowerCase() ?? "";
|
|
@@ -10369,6 +10556,22 @@ function failureOutcome(error) {
|
|
|
10369
10556
|
function failureFromClientResult(result) {
|
|
10370
10557
|
return failureOutcome(opErrorFromClientError(result.error, result.status));
|
|
10371
10558
|
}
|
|
10559
|
+
const MAX_REPORTED_ISSUES = 3;
|
|
10560
|
+
/**
|
|
10561
|
+
* Validate an operation's input with its zod request schema. Returns null when
|
|
10562
|
+
* it is valid, otherwise an `INVALID_REQUEST` failure whose message names the
|
|
10563
|
+
* offending fields and what was expected (zod's issue text, which never
|
|
10564
|
+
* echoes the input value); nothing is sent.
|
|
10565
|
+
*/
|
|
10566
|
+
function validateOpRequest(schema, value) {
|
|
10567
|
+
const parsed = schema.safeParse(value);
|
|
10568
|
+
if (parsed.success) return null;
|
|
10569
|
+
const issues = parsed.error.issues.slice(0, MAX_REPORTED_ISSUES).map((issue) => {
|
|
10570
|
+
return `${issue.path.map((part) => typeof part === "number" ? `[${part}]` : String(part)).join(".").replace(/\.\[/g, "[") || "request"}: ${issue.message}`;
|
|
10571
|
+
});
|
|
10572
|
+
const more = parsed.error.issues.length > MAX_REPORTED_ISSUES ? ` (+${parsed.error.issues.length - MAX_REPORTED_ISSUES} more)` : "";
|
|
10573
|
+
return failureOutcome(opError("INVALID_REQUEST", { message: `Invalid request: ${issues.join("; ")}${more}. Nothing was sent.` }));
|
|
10574
|
+
}
|
|
10372
10575
|
//#endregion
|
|
10373
10576
|
//#region ../shared/src/agentInbox.ts
|
|
10374
10577
|
const NON_MEMBER_MENTION_REPLY_GUIDANCE = "[Raft notice: You were notified as a non-member, so you cannot reply in that channel. If no reply is needed, no action is required. Otherwise, DM the person who mentioned you or join the channel to participate.]";
|
|
@@ -11002,50 +11205,75 @@ const MAX_EXACT_SEQS = 2500;
|
|
|
11002
11205
|
function positiveInt(value) {
|
|
11003
11206
|
return typeof value === "number" && Number.isInteger(value) && value > 0;
|
|
11004
11207
|
}
|
|
11208
|
+
function contextOrNull(value) {
|
|
11209
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
11210
|
+
}
|
|
11005
11211
|
var SeenFrontier = class SeenFrontier {
|
|
11006
|
-
|
|
11007
|
-
|
|
11212
|
+
core = {
|
|
11213
|
+
targets: /* @__PURE__ */ new Map(),
|
|
11214
|
+
aliases: /* @__PURE__ */ new Map(),
|
|
11215
|
+
context: null
|
|
11216
|
+
};
|
|
11217
|
+
/** Set on a view from `inContext`: the context this view books and attests under. */
|
|
11218
|
+
bound = void 0;
|
|
11008
11219
|
static fromSnapshot(snapshot) {
|
|
11009
11220
|
const frontier = new SeenFrontier();
|
|
11010
11221
|
frontier.absorb(snapshot);
|
|
11011
11222
|
return frontier;
|
|
11012
11223
|
}
|
|
11013
|
-
/**
|
|
11224
|
+
/** The model context this frontier books and attests under; null means no context scoping. */
|
|
11225
|
+
get contextId() {
|
|
11226
|
+
return this.bound !== void 0 ? this.bound : this.core.context;
|
|
11227
|
+
}
|
|
11228
|
+
/**
|
|
11229
|
+
* Set the current model context (shared by every view of this frontier that
|
|
11230
|
+
* is not bound to its own context). Call it when the model's context
|
|
11231
|
+
* changes (a new session, a compaction): reads booked under the previous
|
|
11232
|
+
* context then no longer attest a send. `null` turns scoping off.
|
|
11233
|
+
*/
|
|
11234
|
+
setContext(contextId) {
|
|
11235
|
+
this.core.context = contextOrNull(contextId);
|
|
11236
|
+
}
|
|
11237
|
+
/**
|
|
11238
|
+
* A view over the same records that books and attests under `contextId`
|
|
11239
|
+
* (for one call made on behalf of that model context), without changing the
|
|
11240
|
+
* frontier's current context. `undefined` returns this frontier.
|
|
11241
|
+
*/
|
|
11242
|
+
inContext(contextId) {
|
|
11243
|
+
if (contextId === void 0) return this;
|
|
11244
|
+
const view = new SeenFrontier();
|
|
11245
|
+
view.core = this.core;
|
|
11246
|
+
view.bound = contextOrNull(contextId);
|
|
11247
|
+
return view;
|
|
11248
|
+
}
|
|
11249
|
+
/**
|
|
11250
|
+
* Merge a snapshot into this frontier: each record is booked under the
|
|
11251
|
+
* context it was booked in (same context: monotonic, never lowers a mark).
|
|
11252
|
+
* Adopts the snapshot's current context when this frontier has none.
|
|
11253
|
+
*/
|
|
11014
11254
|
absorb(snapshot) {
|
|
11015
11255
|
if (!snapshot || snapshot.version !== 1) return;
|
|
11016
11256
|
for (const [alias, canonical] of Object.entries(snapshot.aliases ?? {})) this.recordAlias(alias, canonical);
|
|
11017
11257
|
for (const [target, state] of Object.entries(snapshot.targets ?? {})) {
|
|
11018
|
-
if (positiveInt(state.upTo)) this.
|
|
11019
|
-
if (Array.isArray(state.exact)) this.
|
|
11258
|
+
if (positiveInt(state.upTo)) this.bookUpTo(target, state.upTo, contextOrNull(state.upToContextId));
|
|
11259
|
+
if (Array.isArray(state.exact)) this.bookExact(target, state.exact, contextOrNull(state.exactContextId));
|
|
11020
11260
|
}
|
|
11261
|
+
if (this.core.context === null) this.core.context = contextOrNull(snapshot.contextId);
|
|
11021
11262
|
}
|
|
11022
11263
|
/** Remember that the Server resolved `requested` to `canonical` (a thread spelling, a `~agent` suffix, …). */
|
|
11023
11264
|
recordAlias(requested, canonical) {
|
|
11024
|
-
if (requested && canonical && requested !== canonical) this.aliases.set(requested, canonical);
|
|
11265
|
+
if (requested && canonical && requested !== canonical) this.core.aliases.set(requested, canonical);
|
|
11025
11266
|
}
|
|
11026
11267
|
canonical(target) {
|
|
11027
|
-
return this.aliases.get(target) ?? target;
|
|
11268
|
+
return this.core.aliases.get(target) ?? target;
|
|
11028
11269
|
}
|
|
11029
|
-
/** Advance the contiguous boundary for a target; never lowers it. */
|
|
11270
|
+
/** Advance the contiguous boundary for a target in the current context; never lowers it within one context. */
|
|
11030
11271
|
recordUpTo(target, seq) {
|
|
11031
|
-
|
|
11032
|
-
const state = this.state(this.canonical(target));
|
|
11033
|
-
if (state.upTo === void 0 || seq > state.upTo) state.upTo = seq;
|
|
11034
|
-
for (const exact of state.exact) if (exact <= state.upTo) state.exact.delete(exact);
|
|
11272
|
+
this.bookUpTo(target, seq, this.contextId);
|
|
11035
11273
|
}
|
|
11036
|
-
/** Record bodies that were rendered without proving contiguity. */
|
|
11274
|
+
/** Record bodies that were rendered without proving contiguity, in the current context. */
|
|
11037
11275
|
recordExact(target, seqs) {
|
|
11038
|
-
|
|
11039
|
-
for (const seq of seqs) {
|
|
11040
|
-
if (!positiveInt(seq)) continue;
|
|
11041
|
-
if (state.upTo !== void 0 && seq <= state.upTo) continue;
|
|
11042
|
-
state.exact.add(seq);
|
|
11043
|
-
}
|
|
11044
|
-
if (state.exact.size > MAX_EXACT_SEQS) {
|
|
11045
|
-
const keep = [...state.exact].sort((a, b) => b - a).slice(0, MAX_EXACT_SEQS);
|
|
11046
|
-
state.exact.clear();
|
|
11047
|
-
for (const seq of keep) state.exact.add(seq);
|
|
11048
|
-
}
|
|
11276
|
+
this.bookExact(target, seqs, this.contextId);
|
|
11049
11277
|
}
|
|
11050
11278
|
/**
|
|
11051
11279
|
* Attest that the model saw the context of an interrupt (`outcome.interrupt`
|
|
@@ -11060,32 +11288,75 @@ var SeenFrontier = class SeenFrontier {
|
|
|
11060
11288
|
this.recordUpTo(held.target, held.seenUpToSeq);
|
|
11061
11289
|
return true;
|
|
11062
11290
|
}
|
|
11063
|
-
/**
|
|
11291
|
+
/**
|
|
11292
|
+
* What to attest on a send or claim to `target`; omits `seenUpToSeq` when
|
|
11293
|
+
* nothing contiguous is known. With a current context, only bookings made
|
|
11294
|
+
* in that context count.
|
|
11295
|
+
*/
|
|
11064
11296
|
attestation(target) {
|
|
11065
|
-
const state = this.targets.get(this.canonical(target));
|
|
11297
|
+
const state = this.core.targets.get(this.canonical(target));
|
|
11066
11298
|
if (!state) return { seenExactSeqs: [] };
|
|
11299
|
+
const context = this.contextId;
|
|
11300
|
+
const upTo = context === null || state.upToContext === context ? state.upTo : void 0;
|
|
11301
|
+
const exact = context === null || state.exactContext === context ? [...state.exact] : [];
|
|
11067
11302
|
return {
|
|
11068
|
-
...
|
|
11069
|
-
seenExactSeqs:
|
|
11303
|
+
...upTo === void 0 ? {} : { seenUpToSeq: upTo },
|
|
11304
|
+
seenExactSeqs: exact.filter((seq) => upTo === void 0 || seq > upTo).sort((a, b) => a - b)
|
|
11070
11305
|
};
|
|
11071
11306
|
}
|
|
11072
11307
|
snapshot() {
|
|
11073
11308
|
const targets = {};
|
|
11074
|
-
for (const [target, state] of this.targets) targets[target] = {
|
|
11309
|
+
for (const [target, state] of this.core.targets) targets[target] = {
|
|
11075
11310
|
...state.upTo === void 0 ? {} : { upTo: state.upTo },
|
|
11076
|
-
...state.exact.size === 0 ? {} : { exact: [...state.exact].sort((a, b) => a - b) }
|
|
11311
|
+
...state.exact.size === 0 ? {} : { exact: [...state.exact].sort((a, b) => a - b) },
|
|
11312
|
+
...state.upTo === void 0 || state.upToContext === null ? {} : { upToContextId: state.upToContext },
|
|
11313
|
+
...state.exact.size === 0 || state.exactContext === null ? {} : { exactContextId: state.exactContext }
|
|
11077
11314
|
};
|
|
11078
11315
|
return {
|
|
11079
11316
|
version: 1,
|
|
11080
11317
|
targets,
|
|
11081
|
-
aliases: Object.fromEntries(this.aliases)
|
|
11318
|
+
aliases: Object.fromEntries(this.core.aliases),
|
|
11319
|
+
...this.core.context === null ? {} : { contextId: this.core.context }
|
|
11082
11320
|
};
|
|
11083
11321
|
}
|
|
11322
|
+
/** A high-water booking (seenPolicy `bookStreamEntries`): another context's record is replaced, not merged. */
|
|
11323
|
+
bookUpTo(target, seq, context) {
|
|
11324
|
+
if (!positiveInt(seq)) return;
|
|
11325
|
+
const state = this.state(this.canonical(target));
|
|
11326
|
+
if (state.exact.size > 0 && state.exactContext !== context) state.exact.clear();
|
|
11327
|
+
if (state.upTo === void 0 || state.upToContext !== context || seq > state.upTo) state.upTo = seq;
|
|
11328
|
+
state.upToContext = context;
|
|
11329
|
+
state.exactContext = context;
|
|
11330
|
+
for (const exact of state.exact) if (exact <= state.upTo) state.exact.delete(exact);
|
|
11331
|
+
}
|
|
11332
|
+
/** A sparse booking (seenPolicy `bookExactSeqs`): merges within a context, replaces across; never moves `upTo`. */
|
|
11333
|
+
bookExact(target, seqs, context) {
|
|
11334
|
+
const state = this.state(this.canonical(target));
|
|
11335
|
+
if (state.exactContext !== context) {
|
|
11336
|
+
state.exact.clear();
|
|
11337
|
+
state.exactContext = context;
|
|
11338
|
+
}
|
|
11339
|
+
const floor = state.upToContext === context ? state.upTo : void 0;
|
|
11340
|
+
for (const seq of seqs) {
|
|
11341
|
+
if (!positiveInt(seq)) continue;
|
|
11342
|
+
if (floor !== void 0 && seq <= floor) continue;
|
|
11343
|
+
state.exact.add(seq);
|
|
11344
|
+
}
|
|
11345
|
+
if (state.exact.size > MAX_EXACT_SEQS) {
|
|
11346
|
+
const keep = [...state.exact].sort((a, b) => b - a).slice(0, MAX_EXACT_SEQS);
|
|
11347
|
+
state.exact.clear();
|
|
11348
|
+
for (const seq of keep) state.exact.add(seq);
|
|
11349
|
+
}
|
|
11350
|
+
}
|
|
11084
11351
|
state(target) {
|
|
11085
|
-
let state = this.targets.get(target);
|
|
11352
|
+
let state = this.core.targets.get(target);
|
|
11086
11353
|
if (!state) {
|
|
11087
|
-
state = {
|
|
11088
|
-
|
|
11354
|
+
state = {
|
|
11355
|
+
upToContext: null,
|
|
11356
|
+
exact: /* @__PURE__ */ new Set(),
|
|
11357
|
+
exactContext: null
|
|
11358
|
+
};
|
|
11359
|
+
this.core.targets.set(target, state);
|
|
11089
11360
|
}
|
|
11090
11361
|
return state;
|
|
11091
11362
|
}
|
|
@@ -11158,7 +11429,18 @@ function latestReadThreadFromCliReadState(raw) {
|
|
|
11158
11429
|
};
|
|
11159
11430
|
}
|
|
11160
11431
|
//#endregion
|
|
11432
|
+
//#region ../shared/src/agentOps/requestSchema.ts
|
|
11433
|
+
function requestSchema() {
|
|
11434
|
+
return (schema) => schema;
|
|
11435
|
+
}
|
|
11436
|
+
//#endregion
|
|
11161
11437
|
//#region ../shared/src/agentOps/inbox.ts
|
|
11438
|
+
const inboxPullFields = {
|
|
11439
|
+
since: number$1().int().nonnegative().optional().describe("Cursor of the previous batch; under cursor acks it acknowledges that batch. Omit to continue from the last committed cursor."),
|
|
11440
|
+
limit: number$1().int().positive().optional().describe("Messages per batch, 1..200 (Server default 50)."),
|
|
11441
|
+
ack: _enum(["cursor", "immediate"]).optional().describe("cursor (default): a batch is acknowledged by the next pull; immediate: acknowledged as it is returned.")
|
|
11442
|
+
};
|
|
11443
|
+
const checkInboxRequestSchema = requestSchema()(object(inboxPullFields));
|
|
11162
11444
|
const MAX_DRAIN_ROUNDS = 50;
|
|
11163
11445
|
function batchNext(batch) {
|
|
11164
11446
|
if (batch.hasMore) return {
|
|
@@ -11197,6 +11479,8 @@ function recordExactSeen(frontier, messages) {
|
|
|
11197
11479
|
}
|
|
11198
11480
|
/** One bounded pull. Records exact seen seqs on `frontier` (sparse drains never advance the high-water mark). */
|
|
11199
11481
|
async function checkInbox(client, request = {}, frontier) {
|
|
11482
|
+
const invalid = validateOpRequest(checkInboxRequestSchema, request);
|
|
11483
|
+
if (invalid) return invalid;
|
|
11200
11484
|
const ack = request.ack ?? "cursor";
|
|
11201
11485
|
const result = await client.events.get({
|
|
11202
11486
|
since: request.since === void 0 ? "latest" : String(request.since),
|
|
@@ -11223,6 +11507,7 @@ async function checkInbox(client, request = {}, frontier) {
|
|
|
11223
11507
|
text: batchText(batch)
|
|
11224
11508
|
};
|
|
11225
11509
|
}
|
|
11510
|
+
const drainInboxRequestSchema = requestSchema()(object(inboxPullFields));
|
|
11226
11511
|
/**
|
|
11227
11512
|
* Pull until the Server reports nothing more, like `raft message check`, but
|
|
11228
11513
|
* as an async iterator so that acknowledgement follows consumption: under
|
|
@@ -11235,6 +11520,15 @@ async function checkInbox(client, request = {}, frontier) {
|
|
|
11235
11520
|
*/
|
|
11236
11521
|
async function* drainInbox(client, request = {}, frontier, onPull) {
|
|
11237
11522
|
const ack = request.ack ?? "cursor";
|
|
11523
|
+
const invalid = validateOpRequest(drainInboxRequestSchema, request);
|
|
11524
|
+
if (invalid) return {
|
|
11525
|
+
cursor: null,
|
|
11526
|
+
ackMode: ack,
|
|
11527
|
+
rounds: 0,
|
|
11528
|
+
hasMore: true,
|
|
11529
|
+
stillUnreadConversations: null,
|
|
11530
|
+
error: invalid
|
|
11531
|
+
};
|
|
11238
11532
|
let cursor = request.since ?? null;
|
|
11239
11533
|
let ackMode = ack;
|
|
11240
11534
|
let stillUnread = null;
|
|
@@ -11295,6 +11589,47 @@ async function* drainInbox(client, request = {}, frontier, onPull) {
|
|
|
11295
11589
|
error: null
|
|
11296
11590
|
};
|
|
11297
11591
|
}
|
|
11592
|
+
/** Fold a finished drain into one outcome; a drain that failed before its first batch is that failure. */
|
|
11593
|
+
function drainedInboxOutcome(batches, summary) {
|
|
11594
|
+
if (summary.error && batches.length === 0) return summary.error;
|
|
11595
|
+
const messages = batches.flatMap((batch) => batch.messages);
|
|
11596
|
+
const lines = [formatAgentMessages(messages.map((m) => toAgentMessageLike(m.raw)))];
|
|
11597
|
+
if (summary.error) lines.push("The drain stopped early on an error; more messages may be pending. Check the inbox again.");
|
|
11598
|
+
else if (summary.hasMore) lines.push("More messages are pending. Check the inbox again.");
|
|
11599
|
+
else if (messages.length > 0) lines.push("No more new inbox messages.");
|
|
11600
|
+
const stillUnread = summary.stillUnreadConversations ?? 0;
|
|
11601
|
+
if (stillUnread > 0) lines.push(formatAgentInboxHint({ unread_conversations: stillUnread }));
|
|
11602
|
+
const first = messages[0];
|
|
11603
|
+
const next = summary.hasMore ? {
|
|
11604
|
+
kind: "check_inbox_again",
|
|
11605
|
+
command: "raft message check",
|
|
11606
|
+
why: "More messages are pending."
|
|
11607
|
+
} : stillUnread > 0 ? {
|
|
11608
|
+
kind: "list_inbox",
|
|
11609
|
+
command: "raft inbox check",
|
|
11610
|
+
why: "Conversations remain unread beyond this drain."
|
|
11611
|
+
} : first ? {
|
|
11612
|
+
kind: "reply_or_act",
|
|
11613
|
+
args: { target: first.target },
|
|
11614
|
+
why: "Handle the messages above; reply where each one came from."
|
|
11615
|
+
} : null;
|
|
11616
|
+
return {
|
|
11617
|
+
ok: true,
|
|
11618
|
+
state: messages.length > 0 ? "batch" : "empty",
|
|
11619
|
+
data: {
|
|
11620
|
+
messages,
|
|
11621
|
+
batches: batches.length,
|
|
11622
|
+
summary
|
|
11623
|
+
},
|
|
11624
|
+
next,
|
|
11625
|
+
text: lines.join("\n")
|
|
11626
|
+
};
|
|
11627
|
+
}
|
|
11628
|
+
const listInboxRequestSchema = requestSchema()(object({
|
|
11629
|
+
view: _enum(["unread", "mentions"]).optional().describe("unread (default): every unread conversation; mentions: only those that @mention you."),
|
|
11630
|
+
before: number$1().int().nonnegative().optional().describe("Next page: the nextBeforeSeq of the previous page."),
|
|
11631
|
+
limit: number$1().int().positive().optional().describe("Conversations per page, 1..50 (Server default 20).")
|
|
11632
|
+
}));
|
|
11298
11633
|
function openCommandFor(item) {
|
|
11299
11634
|
return `raft message read --target "${item.target}" --after ${item.lastReadSeq}`;
|
|
11300
11635
|
}
|
|
@@ -11330,6 +11665,8 @@ function listingText(listing) {
|
|
|
11330
11665
|
}
|
|
11331
11666
|
/** The agent's Activity panel. Lists only; nothing is consumed. */
|
|
11332
11667
|
async function listInbox(client, request = {}) {
|
|
11668
|
+
const invalid = validateOpRequest(listInboxRequestSchema, request);
|
|
11669
|
+
if (invalid) return invalid;
|
|
11333
11670
|
const result = await client.inbox.list({
|
|
11334
11671
|
...request.view && request.view !== "unread" ? { view: request.view } : {},
|
|
11335
11672
|
...request.before === void 0 ? {} : { before_seq: String(request.before) },
|
|
@@ -11364,6 +11701,16 @@ async function listInbox(client, request = {}) {
|
|
|
11364
11701
|
}
|
|
11365
11702
|
//#endregion
|
|
11366
11703
|
//#region ../shared/src/agentOps/messages.ts
|
|
11704
|
+
/** A conversation target as agents spell it. */
|
|
11705
|
+
const raftTargetSchema = string().describe("Conversation: `#channel`, `dm:@peer`, or a thread `#channel:shortid` / `dm:@peer:shortid`.");
|
|
11706
|
+
const readHistoryRequestSchema = requestSchema()(object({
|
|
11707
|
+
target: raftTargetSchema,
|
|
11708
|
+
after: number$1().int().nonnegative().optional().describe("Only messages after this seq (exclusive): read what is new since a position, for example the `--after` an inbox listing prints."),
|
|
11709
|
+
before: number$1().int().nonnegative().optional().describe("Only messages before this seq (exclusive): browse older history."),
|
|
11710
|
+
around: union([number$1().int(), string()]).optional().describe("Anchor a window around a message: its seq number (for example \"12345\") or its message id."),
|
|
11711
|
+
limit: number$1().int().positive().optional().describe("Maximum messages in the window."),
|
|
11712
|
+
consume: boolean().optional().describe("false reads without consuming: the conversation is not marked read and nothing counts as seen.")
|
|
11713
|
+
}));
|
|
11367
11714
|
function historyNext(page) {
|
|
11368
11715
|
const seqs = page.messages.map((m) => m.seq).filter((s) => s !== null);
|
|
11369
11716
|
if (page.hasNewer && seqs.length > 0) {
|
|
@@ -11416,6 +11763,8 @@ function recordHistorySeen(frontier, requested, page, around) {
|
|
|
11416
11763
|
frontier.recordExact(page.target, seqs);
|
|
11417
11764
|
}
|
|
11418
11765
|
async function readHistory(client, request, frontier) {
|
|
11766
|
+
const invalid = validateOpRequest(readHistoryRequestSchema, request);
|
|
11767
|
+
if (invalid) return invalid;
|
|
11419
11768
|
if (!request.target?.trim()) return failureOutcome(opError("INVALID_REQUEST", { message: "A target is required to read history." }));
|
|
11420
11769
|
const result = await client.history.read({
|
|
11421
11770
|
channel: request.target,
|
|
@@ -11446,6 +11795,24 @@ async function readHistory(client, request, frontier) {
|
|
|
11446
11795
|
}
|
|
11447
11796
|
/** Attempts for a keyed send whose request never reached the Server (transport failure only). */
|
|
11448
11797
|
const SEND_TRANSPORT_ATTEMPTS = 3;
|
|
11798
|
+
const sendMessageFields = {
|
|
11799
|
+
content: string().describe("Message text (Markdown). May be empty only when attachmentIds are given."),
|
|
11800
|
+
attachmentIds: array(string()).optional().describe("Attachment ids from an upload, posted with this message."),
|
|
11801
|
+
idempotencyKey: string().optional().describe("One key per logical message; generated when omitted. Reuse it to retry, and to resume an interrupted send (interrupt.resume.idempotencyKey)."),
|
|
11802
|
+
mentions: array(agentApiStructuredMentionSchema).optional().describe("Structured @mentions (user or agent id and name), in addition to @handles in the text."),
|
|
11803
|
+
seen: object({
|
|
11804
|
+
upToSeq: number$1().int().nonnegative().optional(),
|
|
11805
|
+
exactSeqs: array(number$1().int().positive()).optional()
|
|
11806
|
+
}).optional().describe("Override the seen attestation for this send (runtimes that track what their model saw themselves).")
|
|
11807
|
+
};
|
|
11808
|
+
const sendMessageRequestSchema = requestSchema()(object({
|
|
11809
|
+
target: raftTargetSchema,
|
|
11810
|
+
...sendMessageFields
|
|
11811
|
+
}));
|
|
11812
|
+
const replyToRequestSchema = requestSchema()(object({
|
|
11813
|
+
message: object({ target: raftTargetSchema }).describe("The received message to reply to; only its `target` is used."),
|
|
11814
|
+
...sendMessageFields
|
|
11815
|
+
}));
|
|
11449
11816
|
/** Today's held text (unchanged); it is the interrupt's `context` and the outcome's `text`. */
|
|
11450
11817
|
function heldText(held, action) {
|
|
11451
11818
|
const noun = held.newMessageCount === 1 ? "message" : "messages";
|
|
@@ -11509,6 +11876,8 @@ function sentOutcome(target, data) {
|
|
|
11509
11876
|
};
|
|
11510
11877
|
}
|
|
11511
11878
|
async function sendMessage(client, request, frontier) {
|
|
11879
|
+
const invalid = validateOpRequest(sendMessageRequestSchema, request);
|
|
11880
|
+
if (invalid) return invalid;
|
|
11512
11881
|
if (!request.target?.trim()) return failureOutcome(opError("INVALID_REQUEST", { message: "A target is required to send a message." }));
|
|
11513
11882
|
if (typeof request.content !== "string" || !request.content.trim() && !request.attachmentIds?.length) return failureOutcome(opError("INVALID_REQUEST", { message: "Message content (or an attachment) is required." }));
|
|
11514
11883
|
const idempotencyKey = request.idempotencyKey?.trim() || crypto.randomUUID();
|
|
@@ -11712,7 +12081,21 @@ function formatAgentTaskHistory(data) {
|
|
|
11712
12081
|
}
|
|
11713
12082
|
//#endregion
|
|
11714
12083
|
//#region ../shared/src/agentOps/tasks.ts
|
|
12084
|
+
const taskChannelSchema = string().describe("The channel the task board belongs to, for example `#proj-sdk`.");
|
|
12085
|
+
const taskNumberSchema = number$1().int().positive().describe("The task number on that channel's board.");
|
|
12086
|
+
const raftTaskStatusSchema = _enum([
|
|
12087
|
+
"todo",
|
|
12088
|
+
"in_progress",
|
|
12089
|
+
"in_review",
|
|
12090
|
+
"done",
|
|
12091
|
+
"closed"
|
|
12092
|
+
]);
|
|
11715
12093
|
const TASK_CLAIM_REASON_ALREADY_CLAIMED_BY_YOU = "already claimed by you";
|
|
12094
|
+
const claimTasksRequestSchema = requestSchema()(object({
|
|
12095
|
+
target: taskChannelSchema,
|
|
12096
|
+
taskNumbers: array(number$1().int().positive()).optional().describe("Task numbers to claim."),
|
|
12097
|
+
messageIds: array(string()).optional().describe("Full or short ids of top-level messages to claim as tasks.")
|
|
12098
|
+
}));
|
|
11716
12099
|
/**
|
|
11717
12100
|
* A held task call: the interrupt whose resume is the identical command, with
|
|
11718
12101
|
* no cancel (a held task call saved nothing). `context` is today's held text.
|
|
@@ -11761,6 +12144,8 @@ function claimText(claim) {
|
|
|
11761
12144
|
async function claimTasks(client, request) {
|
|
11762
12145
|
const numbers = (request.taskNumbers ?? []).filter((n) => Number.isInteger(n) && n > 0);
|
|
11763
12146
|
const ids = (request.messageIds ?? []).map((id) => id.trim()).filter(Boolean);
|
|
12147
|
+
const invalid = validateOpRequest(claimTasksRequestSchema, request);
|
|
12148
|
+
if (invalid) return invalid;
|
|
11764
12149
|
if (!request.target?.trim()) return failureOutcome(opError("INVALID_REQUEST", { message: "A channel target is required to claim tasks." }));
|
|
11765
12150
|
if (numbers.length === 0 && ids.length === 0) return failureOutcome(opError("INVALID_REQUEST", { message: "Pass at least one task number or message id to claim." }));
|
|
11766
12151
|
const result = await client.tasks.claim({
|
|
@@ -11812,7 +12197,21 @@ async function claimTasks(client, request) {
|
|
|
11812
12197
|
text: claimText(claim)
|
|
11813
12198
|
};
|
|
11814
12199
|
}
|
|
12200
|
+
const listTasksRequestSchema = requestSchema()(object({
|
|
12201
|
+
target: string().optional().describe("A channel's board, for example `#proj-sdk`. Pass exactly one of target or mine."),
|
|
12202
|
+
mine: boolean().optional().describe("true: your own tasks across channels instead of one board."),
|
|
12203
|
+
status: _enum([
|
|
12204
|
+
"todo",
|
|
12205
|
+
"in_progress",
|
|
12206
|
+
"in_review",
|
|
12207
|
+
"done",
|
|
12208
|
+
"closed",
|
|
12209
|
+
"all"
|
|
12210
|
+
]).optional().describe("Filter by status; all includes done and closed.")
|
|
12211
|
+
}));
|
|
11815
12212
|
async function listTasks(client, request) {
|
|
12213
|
+
const invalid = validateOpRequest(listTasksRequestSchema, request);
|
|
12214
|
+
if (invalid) return invalid;
|
|
11816
12215
|
const mine = request.mine === true;
|
|
11817
12216
|
if (mine === Boolean(request.target?.trim())) return failureOutcome(opError("INVALID_REQUEST", { message: "Pass exactly one of a channel target or mine: true." }));
|
|
11818
12217
|
const result = await client.tasks.list({
|
|
@@ -11846,7 +12245,17 @@ async function listTasks(client, request) {
|
|
|
11846
12245
|
text
|
|
11847
12246
|
};
|
|
11848
12247
|
}
|
|
12248
|
+
const createTasksRequestSchema = requestSchema()(object({
|
|
12249
|
+
target: taskChannelSchema,
|
|
12250
|
+
tasks: array(object({
|
|
12251
|
+
title: string().describe("Task title."),
|
|
12252
|
+
createsResource: boolean().optional().describe("The task produces a resource (for example a document) that needs a receipt.")
|
|
12253
|
+
})).describe("One entry per task to create."),
|
|
12254
|
+
assignee: string().optional().describe("`@handle`: yourself to start in_progress, or (owner/admin) someone else to reserve a todo.")
|
|
12255
|
+
}));
|
|
11849
12256
|
async function createTasks(client, request) {
|
|
12257
|
+
const invalid = validateOpRequest(createTasksRequestSchema, request);
|
|
12258
|
+
if (invalid) return invalid;
|
|
11850
12259
|
if (!request.target?.trim() || !request.tasks?.length) return failureOutcome(opError("INVALID_REQUEST", { message: "A channel target and at least one task title are required." }));
|
|
11851
12260
|
const result = await client.tasks.create({
|
|
11852
12261
|
channel: request.target,
|
|
@@ -11875,7 +12284,14 @@ async function createTasks(client, request) {
|
|
|
11875
12284
|
text: formatAgentTasksCreated(request.target, data)
|
|
11876
12285
|
};
|
|
11877
12286
|
}
|
|
11878
|
-
|
|
12287
|
+
const taskRefFields = {
|
|
12288
|
+
target: taskChannelSchema,
|
|
12289
|
+
taskNumber: taskNumberSchema
|
|
12290
|
+
};
|
|
12291
|
+
const taskRefSchema = requestSchema()(object(taskRefFields));
|
|
12292
|
+
function requireTaskRef(request, schema = taskRefSchema) {
|
|
12293
|
+
const invalid = validateOpRequest(schema, request);
|
|
12294
|
+
if (invalid) return invalid;
|
|
11879
12295
|
if (!request.target?.trim() || !Number.isInteger(request.taskNumber) || request.taskNumber <= 0) return failureOutcome(opError("INVALID_REQUEST", { message: "A channel target and a positive task number are required." }));
|
|
11880
12296
|
return null;
|
|
11881
12297
|
}
|
|
@@ -11895,8 +12311,13 @@ async function unclaimTask(client, request) {
|
|
|
11895
12311
|
text: formatAgentTaskUnclaimed(request.taskNumber)
|
|
11896
12312
|
};
|
|
11897
12313
|
}
|
|
12314
|
+
const assignTaskRequestSchema = requestSchema()(object({
|
|
12315
|
+
...taskRefFields,
|
|
12316
|
+
assignee: string().trim().min(1).describe("`@handle` to assign (yourself, or someone else if you are an owner/admin). To clear an assignment use tasks.unassign."),
|
|
12317
|
+
expectedRevision: number$1().int().nonnegative().optional().describe("Revision of the task you just read; the change is refused if it moved.")
|
|
12318
|
+
}));
|
|
11898
12319
|
async function assignTask(client, request) {
|
|
11899
|
-
const invalid = requireTaskRef(request);
|
|
12320
|
+
const invalid = requireTaskRef(request, assignTaskRequestSchema);
|
|
11900
12321
|
if (invalid) return invalid;
|
|
11901
12322
|
const result = await client.tasks.assign({
|
|
11902
12323
|
channel: request.target,
|
|
@@ -11919,6 +12340,38 @@ async function assignTask(client, request) {
|
|
|
11919
12340
|
text: formatAgentTaskAssigned(request.taskNumber, assignee)
|
|
11920
12341
|
};
|
|
11921
12342
|
}
|
|
12343
|
+
const unassignTaskRequestSchema = requestSchema()(object({
|
|
12344
|
+
...taskRefFields,
|
|
12345
|
+
expectedRevision: number$1().int().nonnegative().optional().describe("Revision of the task you just read; the change is refused if it moved.")
|
|
12346
|
+
}));
|
|
12347
|
+
/** Clear a task's assignee (the CLI's `raft task unassign`): the explicit way to clear, never an omitted `assignee`. */
|
|
12348
|
+
async function unassignTask(client, request) {
|
|
12349
|
+
const invalid = requireTaskRef(request, unassignTaskRequestSchema);
|
|
12350
|
+
if (invalid) return invalid;
|
|
12351
|
+
const result = await client.tasks.assign({
|
|
12352
|
+
channel: request.target,
|
|
12353
|
+
task_number: request.taskNumber,
|
|
12354
|
+
assignee: null,
|
|
12355
|
+
...request.expectedRevision === void 0 ? {} : { expected_revision: request.expectedRevision }
|
|
12356
|
+
});
|
|
12357
|
+
if (!result.ok) return failureFromClientResult(result);
|
|
12358
|
+
return {
|
|
12359
|
+
ok: true,
|
|
12360
|
+
state: "unassigned",
|
|
12361
|
+
data: {
|
|
12362
|
+
target: request.target,
|
|
12363
|
+
taskNumber: request.taskNumber,
|
|
12364
|
+
assignee: null,
|
|
12365
|
+
revision: result.data.revision
|
|
12366
|
+
},
|
|
12367
|
+
next: null,
|
|
12368
|
+
text: formatAgentTaskAssigned(request.taskNumber, null)
|
|
12369
|
+
};
|
|
12370
|
+
}
|
|
12371
|
+
const updateTaskStatusRequestSchema = requestSchema()(object({
|
|
12372
|
+
...taskRefFields,
|
|
12373
|
+
status: raftTaskStatusSchema.describe("todo → in_progress → in_review → done; closed from anywhere.")
|
|
12374
|
+
}));
|
|
11922
12375
|
/** A held task write: the interrupt whose resume is the identical command; nothing to cancel. */
|
|
11923
12376
|
function heldTaskWrite(target, data, action, kind, argv) {
|
|
11924
12377
|
const interrupt = heldTaskInterrupt(target, data, action, "After reviewing, repeat the operation if it is still correct.", argv);
|
|
@@ -11936,7 +12389,7 @@ function heldTaskWrite(target, data, action, kind, argv) {
|
|
|
11936
12389
|
};
|
|
11937
12390
|
}
|
|
11938
12391
|
async function updateTaskStatus(client, request) {
|
|
11939
|
-
const invalid = requireTaskRef(request);
|
|
12392
|
+
const invalid = requireTaskRef(request, updateTaskStatusRequestSchema);
|
|
11940
12393
|
if (invalid) return invalid;
|
|
11941
12394
|
const result = await client.tasks.updateStatus({
|
|
11942
12395
|
channel: request.target,
|
|
@@ -11956,6 +12409,11 @@ async function updateTaskStatus(client, request) {
|
|
|
11956
12409
|
text: formatAgentTaskStatusUpdated(request.taskNumber, request.status)
|
|
11957
12410
|
};
|
|
11958
12411
|
}
|
|
12412
|
+
const amendTaskRequestSchema = requestSchema()(object({
|
|
12413
|
+
...taskRefFields,
|
|
12414
|
+
title: string().optional().describe("New title."),
|
|
12415
|
+
description: string().nullable().optional().describe("New description; null clears it.")
|
|
12416
|
+
}));
|
|
11959
12417
|
function taskAmendArgv(request) {
|
|
11960
12418
|
return [
|
|
11961
12419
|
"task",
|
|
@@ -11969,7 +12427,7 @@ function taskAmendArgv(request) {
|
|
|
11969
12427
|
];
|
|
11970
12428
|
}
|
|
11971
12429
|
async function amendTask(client, request) {
|
|
11972
|
-
const invalid = requireTaskRef(request);
|
|
12430
|
+
const invalid = requireTaskRef(request, amendTaskRequestSchema);
|
|
11973
12431
|
if (invalid) return invalid;
|
|
11974
12432
|
if (request.title === void 0 && request.description === void 0) return failureOutcome(opError("INVALID_REQUEST", { message: "Pass a new title, a new description, or description: null to clear it." }));
|
|
11975
12433
|
const result = await client.tasks.amend({
|
|
@@ -12010,7 +12468,13 @@ async function taskHistory(client, request) {
|
|
|
12010
12468
|
text: formatAgentTaskHistory(result.data)
|
|
12011
12469
|
};
|
|
12012
12470
|
}
|
|
12471
|
+
const convertMessageToTaskRequestSchema = requestSchema()(object({
|
|
12472
|
+
target: taskChannelSchema,
|
|
12473
|
+
messageId: string().describe("Full or short id of a top-level message in that channel.")
|
|
12474
|
+
}));
|
|
12013
12475
|
async function convertMessageToTask(client, request) {
|
|
12476
|
+
const invalid = validateOpRequest(convertMessageToTaskRequestSchema, request);
|
|
12477
|
+
if (invalid) return invalid;
|
|
12014
12478
|
if (!request.target?.trim() || !request.messageId?.trim()) return failureOutcome(opError("INVALID_REQUEST", { message: "A channel target and a message id are required." }));
|
|
12015
12479
|
const result = await client.tasks.convert({
|
|
12016
12480
|
channel: request.target,
|
|
@@ -12606,6 +13070,15 @@ function formatAgentProfile(profile, options = {}) {
|
|
|
12606
13070
|
}
|
|
12607
13071
|
//#endregion
|
|
12608
13072
|
//#region ../shared/src/agentOps/channels.ts
|
|
13073
|
+
/** `{ target }` of a regular channel (join, leave, mute, unmute). */
|
|
13074
|
+
const channelTargetRequestSchema = requestSchema()(object({ target: string().describe("A regular channel, `#channel-name`. DMs and threads are not accepted.") }));
|
|
13075
|
+
const channelMembersRequestSchema = requestSchema()(object({ target: string().describe("A channel, DM, or thread target.") }));
|
|
13076
|
+
/** `threads.list` takes no arguments. */
|
|
13077
|
+
const listThreadsRequestSchema = object({});
|
|
13078
|
+
const unfollowThreadRequestSchema = requestSchema()(object({
|
|
13079
|
+
target: string().describe("The thread, for example `#channel:shortid` or `dm:@peer:shortid`."),
|
|
13080
|
+
reason: string().optional().describe("Short reason, kept with the unfollow.")
|
|
13081
|
+
}));
|
|
12609
13082
|
const INVALID_CHANNEL_TARGET = "Target must be a regular channel in the form '#channel-name'. DMs and thread targets are not supported.";
|
|
12610
13083
|
async function resolveRegularChannel(client, target) {
|
|
12611
13084
|
const name = parseRaftRegularChannelTarget$1(target ?? "");
|
|
@@ -12624,6 +13097,8 @@ async function resolveRegularChannel(client, target) {
|
|
|
12624
13097
|
};
|
|
12625
13098
|
}
|
|
12626
13099
|
async function joinChannel(client, request) {
|
|
13100
|
+
const invalid = validateOpRequest(channelTargetRequestSchema, request);
|
|
13101
|
+
if (invalid) return invalid;
|
|
12627
13102
|
const result = await joinRaftChannelByTarget$1(client, { target: request.target });
|
|
12628
13103
|
if (!result.ok) {
|
|
12629
13104
|
const error = result.error;
|
|
@@ -12657,6 +13132,8 @@ async function joinChannel(client, request) {
|
|
|
12657
13132
|
};
|
|
12658
13133
|
}
|
|
12659
13134
|
async function leaveChannel(client, request) {
|
|
13135
|
+
const invalid = validateOpRequest(channelTargetRequestSchema, request);
|
|
13136
|
+
if (invalid) return invalid;
|
|
12660
13137
|
const channel = await resolveRegularChannel(client, request.target);
|
|
12661
13138
|
if ("ok" in channel) return channel;
|
|
12662
13139
|
if (!channel.joined) return {
|
|
@@ -12689,6 +13166,8 @@ function formatSeq(value) {
|
|
|
12689
13166
|
return value == null ? "none" : String(value);
|
|
12690
13167
|
}
|
|
12691
13168
|
async function setChannelMute(client, request, action) {
|
|
13169
|
+
const invalid = validateOpRequest(channelTargetRequestSchema, request);
|
|
13170
|
+
if (invalid) return invalid;
|
|
12692
13171
|
const channel = await resolveRegularChannel(client, request.target);
|
|
12693
13172
|
if ("ok" in channel) return channel;
|
|
12694
13173
|
const result = action === "mute" ? await client.channels.mute({ channelId: channel.id }, {}) : await client.channels.unmute({ channelId: channel.id });
|
|
@@ -12731,6 +13210,8 @@ function unmuteChannel(client, request) {
|
|
|
12731
13210
|
return setChannelMute(client, request, "unmute");
|
|
12732
13211
|
}
|
|
12733
13212
|
async function channelMembers(client, request) {
|
|
13213
|
+
const invalid = validateOpRequest(channelMembersRequestSchema, request);
|
|
13214
|
+
if (invalid) return invalid;
|
|
12734
13215
|
if (!request.target?.trim()) return failureOutcome(opError("INVALID_REQUEST", { message: "A channel, DM, or thread target is required." }));
|
|
12735
13216
|
const result = await client.channels.members({ channel: request.target });
|
|
12736
13217
|
if (!result.ok) return failureFromClientResult(result);
|
|
@@ -12755,6 +13236,8 @@ async function listThreads(client) {
|
|
|
12755
13236
|
};
|
|
12756
13237
|
}
|
|
12757
13238
|
async function unfollowThread(client, request) {
|
|
13239
|
+
const invalid = validateOpRequest(unfollowThreadRequestSchema, request);
|
|
13240
|
+
if (invalid) return invalid;
|
|
12758
13241
|
if (!request.target?.includes(":")) return failureOutcome(opError("INVALID_REQUEST", { message: "A thread target is required, for example '#channel:shortid' or 'dm:@peer:shortid'." }));
|
|
12759
13242
|
const result = await client.threads.unfollow({
|
|
12760
13243
|
thread: request.target,
|
|
@@ -12771,6 +13254,21 @@ async function unfollowThread(client, request) {
|
|
|
12771
13254
|
}
|
|
12772
13255
|
//#endregion
|
|
12773
13256
|
//#region ../shared/src/agentOps/server.ts
|
|
13257
|
+
const serverInfoRequestSchema = requestSchema()(object({
|
|
13258
|
+
view: _enum([
|
|
13259
|
+
"summary",
|
|
13260
|
+
"full",
|
|
13261
|
+
"channels",
|
|
13262
|
+
"agents",
|
|
13263
|
+
"humans"
|
|
13264
|
+
]).optional().describe("summary (default): counts; full: the whole overview; channels / agents / humans: one paged section."),
|
|
13265
|
+
offset: number$1().optional().describe("Section paging: rows to skip."),
|
|
13266
|
+
limit: number$1().optional().describe("Section paging: rows per page (default 50)."),
|
|
13267
|
+
joined: boolean().optional().describe("channels view only: only channels you have joined.")
|
|
13268
|
+
}));
|
|
13269
|
+
const showProfileRequestSchema = requestSchema()(object({ target: string().optional().describe("`@handle` of someone else; omit for your own profile.") }));
|
|
13270
|
+
/** The Agent API's profile body schema; at least one field is required (checked by the operation). */
|
|
13271
|
+
const updateProfileRequestSchema = agentApiProfileUpdateBodySchema;
|
|
12774
13272
|
function nextCommand(section, request, offset, limit, total) {
|
|
12775
13273
|
const nextOffset = offset + limit;
|
|
12776
13274
|
if (nextOffset >= total) return void 0;
|
|
@@ -12784,6 +13282,8 @@ function nextCommand(section, request, offset, limit, total) {
|
|
|
12784
13282
|
return parts.join(" ");
|
|
12785
13283
|
}
|
|
12786
13284
|
async function serverInfo(client, request = {}) {
|
|
13285
|
+
const invalid = validateOpRequest(serverInfoRequestSchema, request);
|
|
13286
|
+
if (invalid) return invalid;
|
|
12787
13287
|
const result = await client.server.info();
|
|
12788
13288
|
if (!result.ok) return failureFromClientResult(result);
|
|
12789
13289
|
const server = result.data;
|
|
@@ -12849,6 +13349,8 @@ async function serverInfo(client, request = {}) {
|
|
|
12849
13349
|
};
|
|
12850
13350
|
}
|
|
12851
13351
|
async function showProfile(client, request = {}, options = {}) {
|
|
13352
|
+
const invalid = validateOpRequest(showProfileRequestSchema, request);
|
|
13353
|
+
if (invalid) return invalid;
|
|
12852
13354
|
const result = await client.profile.show(request.target ? { target: request.target } : {});
|
|
12853
13355
|
if (!result.ok) return failureFromClientResult(result);
|
|
12854
13356
|
return {
|
|
@@ -12860,6 +13362,8 @@ async function showProfile(client, request = {}, options = {}) {
|
|
|
12860
13362
|
};
|
|
12861
13363
|
}
|
|
12862
13364
|
async function updateProfile(client, request, options = {}) {
|
|
13365
|
+
const invalid = validateOpRequest(updateProfileRequestSchema, request);
|
|
13366
|
+
if (invalid) return invalid;
|
|
12863
13367
|
if (request.displayName === void 0 && request.description === void 0 && request.avatarUrl === void 0) return failureOutcome(opError("INVALID_REQUEST", { message: "Pass a displayName, description, or avatarUrl to update." }));
|
|
12864
13368
|
const result = await client.profile.update(request);
|
|
12865
13369
|
if (!result.ok) return failureFromClientResult(result);
|
|
@@ -12913,6 +13417,10 @@ function formatAgentAttachmentComments(attachmentId, comments, threadChannelId)
|
|
|
12913
13417
|
}
|
|
12914
13418
|
//#endregion
|
|
12915
13419
|
//#region ../shared/src/agentOps/attachments.ts
|
|
13420
|
+
const attachmentCommentsRequestSchema = requestSchema()(object({
|
|
13421
|
+
attachmentId: string().describe("The attachment id."),
|
|
13422
|
+
limit: number$1().int().positive().optional().describe("Maximum comments to return.")
|
|
13423
|
+
}));
|
|
12916
13424
|
/** Compatibility fallback for Servers that predate the capability endpoint (the CLI's constant). */
|
|
12917
13425
|
const AGENT_ATTACHMENT_UPLOAD_FALLBACK_MAX_BYTES = 52428800;
|
|
12918
13426
|
const FILENAME_MIME_MAP = {
|
|
@@ -13120,6 +13628,8 @@ async function downloadAttachment(client, request) {
|
|
|
13120
13628
|
};
|
|
13121
13629
|
}
|
|
13122
13630
|
async function attachmentComments(client, request) {
|
|
13631
|
+
const invalid = validateOpRequest(attachmentCommentsRequestSchema, request);
|
|
13632
|
+
if (invalid) return invalid;
|
|
13123
13633
|
if (!request.attachmentId?.trim()) return failureOutcome(opError("INVALID_REQUEST", { message: "An attachment id is required." }));
|
|
13124
13634
|
const result = await client.attachments.comments({ attachmentId: request.attachmentId }, request.limit === void 0 ? {} : { limit: String(request.limit) });
|
|
13125
13635
|
if (!result.ok) return failureFromClientResult(result);
|
|
@@ -13136,101 +13646,12 @@ async function attachmentComments(client, request) {
|
|
|
13136
13646
|
};
|
|
13137
13647
|
}
|
|
13138
13648
|
//#endregion
|
|
13139
|
-
//#region ../shared/src/zonedTime.ts
|
|
13140
|
-
const MS_PER_SECOND = 1e3;
|
|
13141
|
-
const MS_PER_MINUTE = 6e4;
|
|
13142
|
-
const MS_PER_DAY = 864e5;
|
|
13143
|
-
const formatters = /* @__PURE__ */ new Map();
|
|
13144
|
-
function formatterFor(timeZone) {
|
|
13145
|
-
let formatter = formatters.get(timeZone);
|
|
13146
|
-
if (!formatter) {
|
|
13147
|
-
formatter = new Intl.DateTimeFormat("en-US", {
|
|
13148
|
-
timeZone,
|
|
13149
|
-
hourCycle: "h23",
|
|
13150
|
-
era: "short",
|
|
13151
|
-
year: "numeric",
|
|
13152
|
-
month: "numeric",
|
|
13153
|
-
day: "numeric",
|
|
13154
|
-
hour: "numeric",
|
|
13155
|
-
minute: "numeric",
|
|
13156
|
-
second: "numeric"
|
|
13157
|
-
});
|
|
13158
|
-
formatters.set(timeZone, formatter);
|
|
13159
|
-
}
|
|
13160
|
-
return formatter;
|
|
13161
|
-
}
|
|
13162
|
-
/** Days from 1970-01-01 to the proleptic Gregorian date (H. Hinnant's days_from_civil). */
|
|
13163
|
-
function daysFromCivil(year, month, day) {
|
|
13164
|
-
const y = month <= 2 ? year - 1 : year;
|
|
13165
|
-
const era = Math.floor(y / 400);
|
|
13166
|
-
const yoe = y - era * 400;
|
|
13167
|
-
const mp = (month + 9) % 12;
|
|
13168
|
-
const doy = Math.floor((153 * mp + 2) / 5) + day - 1;
|
|
13169
|
-
const doe = yoe * 365 + Math.floor(yoe / 4) - Math.floor(yoe / 100) + doy;
|
|
13170
|
-
return era * 146097 + doe - 719468;
|
|
13171
|
-
}
|
|
13172
|
-
/** The wall clock as milliseconds on a UTC timeline (no Date, so no 0-99 year quirk or range limit). */
|
|
13173
|
-
function wallClockMs(wall) {
|
|
13174
|
-
return daysFromCivil(wall.year, wall.month, wall.day) * MS_PER_DAY + wall.hour * 36e5 + wall.minute * MS_PER_MINUTE + wall.second * MS_PER_SECOND + wall.millisecond;
|
|
13175
|
-
}
|
|
13176
|
-
function floorMod(value, modulus) {
|
|
13177
|
-
return (value % modulus + modulus) % modulus;
|
|
13178
|
-
}
|
|
13179
|
-
/** Wall clock of `epochMs` in `timeZone` plus the exact offset in ms (seconds precision). */
|
|
13180
|
-
function wallClockAt(epochMs, timeZone) {
|
|
13181
|
-
const parts = {};
|
|
13182
|
-
for (const part of formatterFor(timeZone).formatToParts(epochMs)) parts[part.type] = part.value;
|
|
13183
|
-
const eraYear = Number(parts.year);
|
|
13184
|
-
const wall = {
|
|
13185
|
-
year: /^b/i.test(parts.era ?? "") ? 1 - eraYear : eraYear,
|
|
13186
|
-
month: Number(parts.month),
|
|
13187
|
-
day: Number(parts.day),
|
|
13188
|
-
hour: Number(parts.hour),
|
|
13189
|
-
minute: Number(parts.minute),
|
|
13190
|
-
second: Number(parts.second),
|
|
13191
|
-
millisecond: floorMod(epochMs, MS_PER_SECOND)
|
|
13192
|
-
};
|
|
13193
|
-
return {
|
|
13194
|
-
wall,
|
|
13195
|
-
offsetMs: wallClockMs(wall) - epochMs
|
|
13196
|
-
};
|
|
13197
|
-
}
|
|
13198
|
-
function offsetMinutesOf(offsetMs) {
|
|
13199
|
-
return -Math.trunc(-offsetMs / MS_PER_MINUTE);
|
|
13200
|
-
}
|
|
13201
|
-
/** The wall clock and getTimezoneOffset-style offset of `epochMs` in `timeZone`. */
|
|
13202
|
-
function zonedInstant(epochMs, timeZone) {
|
|
13203
|
-
const { wall, offsetMs } = wallClockAt(epochMs, timeZone);
|
|
13204
|
-
return {
|
|
13205
|
-
...wall,
|
|
13206
|
-
offsetMinutes: offsetMinutesOf(offsetMs)
|
|
13207
|
-
};
|
|
13208
|
-
}
|
|
13209
|
-
function pad2(n) {
|
|
13210
|
-
return String(n).padStart(2, "0");
|
|
13211
|
-
}
|
|
13212
|
-
/** `+08:00` / `-04:00` / `+05:30`, as the CLI has always printed offsets. */
|
|
13213
|
-
function formatUtcOffset(offsetMinutes) {
|
|
13214
|
-
const sign = offsetMinutes >= 0 ? "+" : "-";
|
|
13215
|
-
const abs = Math.abs(offsetMinutes);
|
|
13216
|
-
return `${sign}${pad2(Math.floor(abs / 60))}:${pad2(abs % 60)}`;
|
|
13217
|
-
}
|
|
13218
|
-
//#endregion
|
|
13219
13649
|
//#region ../shared/src/agentText/search.ts
|
|
13220
|
-
/**
|
|
13221
|
-
|
|
13222
|
-
* `timeZone` (an IANA name) the time is rendered in that zone; without it, in
|
|
13223
|
-
* the process's local zone (the SDK's historical behaviour). The two agree
|
|
13224
|
-
* byte for byte when `timeZone` is the process zone.
|
|
13225
|
-
*/
|
|
13226
|
-
function formatLocalTimeWithOffset(iso, timeZone) {
|
|
13650
|
+
/** Local wall-clock rendering with offset, as the CLI prints search hit times. */
|
|
13651
|
+
function formatLocalTimeWithOffset(iso) {
|
|
13227
13652
|
const d = new Date(iso);
|
|
13228
13653
|
if (isNaN(d.getTime())) return iso;
|
|
13229
13654
|
const pad = (n) => String(n).padStart(2, "0");
|
|
13230
|
-
if (timeZone !== void 0) {
|
|
13231
|
-
const z = zonedInstant(d.getTime(), timeZone);
|
|
13232
|
-
return `${z.year}-${pad(z.month)}-${pad(z.day)} ${pad(z.hour)}:${pad(z.minute)}:${pad(z.second)} ${formatUtcOffset(z.offsetMinutes)}`;
|
|
13233
|
-
}
|
|
13234
13655
|
const offsetMinutes = -d.getTimezoneOffset();
|
|
13235
13656
|
const sign = offsetMinutes >= 0 ? "+" : "-";
|
|
13236
13657
|
const absOffset = Math.abs(offsetMinutes);
|
|
@@ -13353,11 +13774,8 @@ function renderSearchPreview(content, query) {
|
|
|
13353
13774
|
trailingOmit
|
|
13354
13775
|
].join("");
|
|
13355
13776
|
}
|
|
13356
|
-
/**
|
|
13357
|
-
|
|
13358
|
-
* `timeZone` when given, else in the process's local zone.
|
|
13359
|
-
*/
|
|
13360
|
-
function formatAgentSearchResults(query, data, offset, sort, limit, timeZone) {
|
|
13777
|
+
/** Search results with <match>/<omit /> preview markup. */
|
|
13778
|
+
function formatAgentSearchResults(query, data, offset, sort, limit) {
|
|
13361
13779
|
const trimmedQuery = query.trim();
|
|
13362
13780
|
const oldestShown = data.results?.length ? data.results[data.results.length - 1]?.createdAt : void 0;
|
|
13363
13781
|
const nextPageHint = sort === "recent" && oldestShown ? `page with --before ${oldestShown} (pages OLDER only; NOT a complete traversal: stored times are microsecond but this key is millisecond, so rows inside the boundary millisecond can be skipped, and a full page sharing one timestamp can repeat indefinitely; copy the key verbatim)` : `page with --offset ${(offset ?? 0) + (data.results?.length ?? 0)}`;
|
|
@@ -13377,7 +13795,7 @@ function formatAgentSearchResults(query, data, offset, sort, limit, timeZone) {
|
|
|
13377
13795
|
`<result ref="${ref}">`,
|
|
13378
13796
|
`Source: ${renderSearchSource(result)}`,
|
|
13379
13797
|
`Sender: ${sender}${senderType}`,
|
|
13380
|
-
`Time: ${result.createdAt ? formatLocalTimeWithOffset(result.createdAt
|
|
13798
|
+
`Time: ${result.createdAt ? formatLocalTimeWithOffset(result.createdAt) : "-"}`,
|
|
13381
13799
|
...taskProjection ? [taskProjection] : [],
|
|
13382
13800
|
"",
|
|
13383
13801
|
"<preview>",
|
|
@@ -13398,7 +13816,24 @@ function formatAgentSearchResults(query, data, offset, sort, limit, timeZone) {
|
|
|
13398
13816
|
}
|
|
13399
13817
|
//#endregion
|
|
13400
13818
|
//#region ../shared/src/agentOps/search.ts
|
|
13819
|
+
const searchMessagesRequestSchema = requestSchema()(object({
|
|
13820
|
+
query: string().optional().describe("Free-text query; may be omitted when filtering by target or sender."),
|
|
13821
|
+
target: string().optional().describe("Only this channel, DM, or thread."),
|
|
13822
|
+
sender: string().optional().describe("Only messages from this handle."),
|
|
13823
|
+
sort: _enum(["relevance", "recent"]).optional(),
|
|
13824
|
+
before: string().optional().describe("ISO timestamp; only messages at or before it."),
|
|
13825
|
+
after: string().optional().describe("ISO timestamp; only messages at or after it."),
|
|
13826
|
+
limit: number$1().int().positive().optional().describe("Results per page, 1..50 (Server default 20)."),
|
|
13827
|
+
offset: number$1().int().nonnegative().optional().describe("Skip this many results (paging).")
|
|
13828
|
+
}));
|
|
13829
|
+
const resolveMessageRequestSchema = requestSchema()(object({ messageId: string().describe("Full or short message id.") }));
|
|
13830
|
+
const reactRequestSchema = requestSchema()(object({
|
|
13831
|
+
messageId: string().describe("Full or short message id."),
|
|
13832
|
+
emoji: string().describe("One reaction emoji.")
|
|
13833
|
+
}));
|
|
13401
13834
|
async function searchMessages(client, request) {
|
|
13835
|
+
const invalid = validateOpRequest(searchMessagesRequestSchema, request);
|
|
13836
|
+
if (invalid) return invalid;
|
|
13402
13837
|
if (!request.query?.trim() && !request.target && !request.sender) return failureOutcome(opError("INVALID_REQUEST", { message: "Pass a query, or filter by target or sender." }));
|
|
13403
13838
|
const result = await client.messages.search({
|
|
13404
13839
|
...request.query?.trim() ? { q: request.query.trim() } : {},
|
|
@@ -13438,6 +13873,8 @@ async function searchMessages(client, request) {
|
|
|
13438
13873
|
};
|
|
13439
13874
|
}
|
|
13440
13875
|
async function resolveMessage(client, request) {
|
|
13876
|
+
const invalid = validateOpRequest(resolveMessageRequestSchema, request);
|
|
13877
|
+
if (invalid) return invalid;
|
|
13441
13878
|
if (!request.messageId?.trim()) return failureOutcome(opError("INVALID_REQUEST", { message: "A message id is required." }));
|
|
13442
13879
|
const result = await client.messages.resolve({ msgId: request.messageId });
|
|
13443
13880
|
if (!result.ok) return failureFromClientResult(result);
|
|
@@ -13460,6 +13897,8 @@ async function resolveMessage(client, request) {
|
|
|
13460
13897
|
};
|
|
13461
13898
|
}
|
|
13462
13899
|
async function reactToMessage(client, request, action = "add") {
|
|
13900
|
+
const invalid = validateOpRequest(reactRequestSchema, request);
|
|
13901
|
+
if (invalid) return invalid;
|
|
13463
13902
|
const emoji = request.emoji?.trim();
|
|
13464
13903
|
if (!request.messageId?.trim() || !emoji || /\s/.test(emoji) || emoji.length > 16) return failureOutcome(opError("INVALID_REQUEST", { message: "A message id and a single reaction emoji are required." }));
|
|
13465
13904
|
const params = { msgId: request.messageId };
|
|
@@ -13661,7 +14100,15 @@ function formatAgentSenderMentionDeliveries(messageId, deliveries) {
|
|
|
13661
14100
|
}
|
|
13662
14101
|
//#endregion
|
|
13663
14102
|
//#region ../shared/src/agentOps/mentions.ts
|
|
14103
|
+
const pendingMentionActionsRequestSchema = requestSchema()(object({ limit: number$1().int().positive().optional().describe("Maximum pending mentions to list.") }));
|
|
14104
|
+
const executeMentionActionRequestSchema = requestSchema()(object({
|
|
14105
|
+
action: _enum(["notify", "add"]).describe("notify: tell the mentioned target about the message; add: add them to the conversation."),
|
|
14106
|
+
resolutionIds: array(string()).describe("resolutionId values from mentions.pending.")
|
|
14107
|
+
}));
|
|
14108
|
+
const senderMentionDeliveriesRequestSchema = requestSchema()(object({ messageId: string().describe("Id of a message you sent.") }));
|
|
13664
14109
|
async function pendingMentionActions(client, request = {}) {
|
|
14110
|
+
const invalid = validateOpRequest(pendingMentionActionsRequestSchema, request);
|
|
14111
|
+
if (invalid) return invalid;
|
|
13665
14112
|
const result = await client.mentions.pendingActions(request.limit === void 0 ? {} : { limit: String(request.limit) });
|
|
13666
14113
|
if (!result.ok) return failureFromClientResult(result);
|
|
13667
14114
|
const actions = normalizeAgentPendingMentionActions(result.data);
|
|
@@ -13693,6 +14140,8 @@ async function pendingMentionActions(client, request = {}) {
|
|
|
13693
14140
|
};
|
|
13694
14141
|
}
|
|
13695
14142
|
async function executeMentionAction(client, request) {
|
|
14143
|
+
const invalid = validateOpRequest(executeMentionActionRequestSchema, request);
|
|
14144
|
+
if (invalid) return invalid;
|
|
13696
14145
|
const ids = (request.resolutionIds ?? []).map((id) => id.trim()).filter(Boolean);
|
|
13697
14146
|
if (ids.length === 0 || request.action !== "notify" && request.action !== "add") return failureOutcome(opError("INVALID_REQUEST", { message: "Pass an action (notify | add) and at least one resolution id." }));
|
|
13698
14147
|
const result = await client.mentions.executeAction({
|
|
@@ -13713,6 +14162,8 @@ async function executeMentionAction(client, request) {
|
|
|
13713
14162
|
};
|
|
13714
14163
|
}
|
|
13715
14164
|
async function senderMentionDeliveries(client, request) {
|
|
14165
|
+
const invalid = validateOpRequest(senderMentionDeliveriesRequestSchema, request);
|
|
14166
|
+
if (invalid) return invalid;
|
|
13716
14167
|
if (!request.messageId?.trim()) return failureOutcome(opError("INVALID_REQUEST", { message: "A message id is required." }));
|
|
13717
14168
|
const result = await client.mentions.senderDeliveries({ messageId: request.messageId });
|
|
13718
14169
|
if (!result.ok) return failureFromClientResult(result);
|
|
@@ -13745,11 +14196,26 @@ function formatAgentKnowledgeStdout(content) {
|
|
|
13745
14196
|
}
|
|
13746
14197
|
//#endregion
|
|
13747
14198
|
//#region ../shared/src/agentOps/manual.ts
|
|
14199
|
+
const manualContextFields = {
|
|
14200
|
+
intent: string().min(12).max(500).describe("What the user wants to accomplish with Raft, in a few words. Never a prompt, credential, URL, or message text."),
|
|
14201
|
+
reason: string().min(12).max(500).describe("Why the Manual is needed now, in a few words. Same rules as intent.")
|
|
14202
|
+
};
|
|
14203
|
+
const getManualTopicRequestSchema = requestSchema()(object({
|
|
14204
|
+
topic: string().describe("Topic id (slug), for example one returned by manual.search."),
|
|
14205
|
+
...manualContextFields
|
|
14206
|
+
}));
|
|
14207
|
+
const searchManualRequestSchema = requestSchema()(object({
|
|
14208
|
+
query: string().describe("Search keywords."),
|
|
14209
|
+
scope: string().optional().describe("Restrict the search to one Manual section."),
|
|
14210
|
+
...manualContextFields
|
|
14211
|
+
}));
|
|
13748
14212
|
function requireContext(context) {
|
|
13749
14213
|
if (!context?.intent?.trim() || !context?.reason?.trim()) return failureOutcome(opError("INVALID_REQUEST", { message: "Manual calls need a short `intent` and `reason`." }));
|
|
13750
14214
|
return null;
|
|
13751
14215
|
}
|
|
13752
14216
|
async function getManualTopic(client, request) {
|
|
14217
|
+
const malformed = validateOpRequest(getManualTopicRequestSchema, request);
|
|
14218
|
+
if (malformed) return malformed;
|
|
13753
14219
|
const invalid = requireContext(request);
|
|
13754
14220
|
if (invalid) return invalid;
|
|
13755
14221
|
if (!request.topic?.trim()) return failureOutcome(opError("INVALID_REQUEST", { message: "A topic id is required." }));
|
|
@@ -13768,6 +14234,8 @@ async function getManualTopic(client, request) {
|
|
|
13768
14234
|
};
|
|
13769
14235
|
}
|
|
13770
14236
|
async function searchManual(client, request) {
|
|
14237
|
+
const malformed = validateOpRequest(searchManualRequestSchema, request);
|
|
14238
|
+
if (malformed) return malformed;
|
|
13771
14239
|
const invalid = requireContext(request);
|
|
13772
14240
|
if (invalid) return invalid;
|
|
13773
14241
|
if (!request.query?.trim()) return failureOutcome(opError("INVALID_REQUEST", { message: "Search keywords are required." }));
|
|
@@ -13956,14 +14424,63 @@ var RaftStateSession = class {
|
|
|
13956
14424
|
}
|
|
13957
14425
|
};
|
|
13958
14426
|
//#endregion
|
|
14427
|
+
//#region ../shared/src/agentOps/seenPolicy/consumedSeqs.ts
|
|
14428
|
+
function getParentTargetForThread(target) {
|
|
14429
|
+
if (target.startsWith("dm:@")) {
|
|
14430
|
+
const separatorIndex = target.indexOf(":", 4);
|
|
14431
|
+
return separatorIndex > 0 ? target.slice(0, separatorIndex) : null;
|
|
14432
|
+
}
|
|
14433
|
+
if (target.startsWith("#")) {
|
|
14434
|
+
const separatorIndex = target.indexOf(":");
|
|
14435
|
+
return separatorIndex > 0 ? target.slice(0, separatorIndex) : null;
|
|
14436
|
+
}
|
|
14437
|
+
return null;
|
|
14438
|
+
}
|
|
14439
|
+
//#endregion
|
|
13959
14440
|
//#region ../shared/src/agentOps/actions.ts
|
|
14441
|
+
/**
|
|
14442
|
+
* The Agent API's own body schema (a discriminated union over the card
|
|
14443
|
+
* types), so the SDK checks exactly what the Server checks. The operation
|
|
14444
|
+
* manifest projects it flattened (see the SDK's operations.ts).
|
|
14445
|
+
*/
|
|
14446
|
+
const prepareActionCardRequestSchema = agentApiActionPrepareBodySchema;
|
|
13960
14447
|
/** Text is the CLI's `raft action prepare` output. */
|
|
13961
14448
|
function formatAgentActionCardPosted(target, messageId) {
|
|
13962
14449
|
const shortId = messageId ? messageId.slice(0, 8) : null;
|
|
13963
14450
|
return shortId ? `Action card posted to ${target} as message ${messageId} (short ${shortId}). The human can click the action verb to commit.\n` : `Action card posted to ${target}.\n`;
|
|
13964
14451
|
}
|
|
14452
|
+
/**
|
|
14453
|
+
* Where the card's outcome arrives (Server #8604): when a human executes the
|
|
14454
|
+
* card, or execution fails, the Server posts a system reply that @mentions
|
|
14455
|
+
* the preparer in the card's own thread; a card posted inside a thread has no
|
|
14456
|
+
* thread of its own, so the reply lands in that same thread.
|
|
14457
|
+
*/
|
|
14458
|
+
function awaitConfirmationNext(target, messageId) {
|
|
14459
|
+
const short = messageId.slice(0, 8);
|
|
14460
|
+
if (getParentTargetForThread(target) !== null) return {
|
|
14461
|
+
kind: "await_confirmation",
|
|
14462
|
+
command: `raft message read --target "${target}" --around ${short}`,
|
|
14463
|
+
args: {
|
|
14464
|
+
target,
|
|
14465
|
+
messageId
|
|
14466
|
+
},
|
|
14467
|
+
why: "A human must click the card to commit it. When it is executed (or fails), the outcome arrives as a reply in this thread that @mentions you; you do not need to poll."
|
|
14468
|
+
};
|
|
14469
|
+
const thread = agentTaskThreadTarget(target, messageId);
|
|
14470
|
+
return {
|
|
14471
|
+
kind: "await_confirmation",
|
|
14472
|
+
command: `raft message read --target "${thread}"`,
|
|
14473
|
+
args: {
|
|
14474
|
+
target: thread,
|
|
14475
|
+
messageId
|
|
14476
|
+
},
|
|
14477
|
+
why: "A human must click the card to commit it. When it is executed (or fails), the outcome arrives as a reply in the card's thread that @mentions you; you do not need to poll."
|
|
14478
|
+
};
|
|
14479
|
+
}
|
|
13965
14480
|
async function prepareActionCard(client, request) {
|
|
13966
|
-
if (
|
|
14481
|
+
if (typeof request?.target !== "string" || !request.target.trim() || !request.action) return failureOutcome(opError("INVALID_REQUEST", { message: "A target and an action are required to prepare a card." }));
|
|
14482
|
+
const invalid = validateOpRequest(prepareActionCardRequestSchema, request);
|
|
14483
|
+
if (invalid) return invalid;
|
|
13967
14484
|
const result = await client.actions.prepare(request);
|
|
13968
14485
|
if (!result.ok) return failureFromClientResult(result);
|
|
13969
14486
|
const card = {
|
|
@@ -13974,15 +14491,7 @@ async function prepareActionCard(client, request) {
|
|
|
13974
14491
|
ok: true,
|
|
13975
14492
|
state: "prepared",
|
|
13976
14493
|
data: card,
|
|
13977
|
-
next:
|
|
13978
|
-
kind: "await_confirmation",
|
|
13979
|
-
command: `raft message read --target "${request.target}" --around ${card.messageId.slice(0, 8)}`,
|
|
13980
|
-
args: {
|
|
13981
|
-
target: request.target,
|
|
13982
|
-
messageId: card.messageId
|
|
13983
|
-
},
|
|
13984
|
-
why: "A human must click the card to commit it. You are not notified of the result yet; read the card message to see its state instead of waiting."
|
|
13985
|
-
},
|
|
14494
|
+
next: awaitConfirmationNext(request.target, card.messageId),
|
|
13986
14495
|
text: formatAgentActionCardPosted(request.target, card.messageId)
|
|
13987
14496
|
};
|
|
13988
14497
|
}
|
|
@@ -14042,6 +14551,695 @@ async function readLatestReadThread(options = {}) {
|
|
|
14042
14551
|
return latestReadThreadFromCliReadState(raw);
|
|
14043
14552
|
}
|
|
14044
14553
|
//#endregion
|
|
14554
|
+
//#region src/operationSchemas.ts
|
|
14555
|
+
/** `identity.whoami` takes no arguments. */
|
|
14556
|
+
const whoamiRequestSchema = object({});
|
|
14557
|
+
/** `inbox.commit`: no cursor commits the pending batch; a cursor commits up to it. */
|
|
14558
|
+
const commitInboxRequestSchema = requestSchema()(object({ cursor: number$1().int().nonnegative().nullable().optional().describe("Commit up to this batch cursor; omit to commit the batch the last inbox pull returned.") }));
|
|
14559
|
+
//#endregion
|
|
14560
|
+
//#region src/toolSchema.ts
|
|
14561
|
+
/** Every keyword the subset allows. */
|
|
14562
|
+
const RAFT_JSON_SCHEMA_KEYWORDS = [
|
|
14563
|
+
"type",
|
|
14564
|
+
"description",
|
|
14565
|
+
"properties",
|
|
14566
|
+
"required",
|
|
14567
|
+
"items",
|
|
14568
|
+
"enum",
|
|
14569
|
+
"minimum",
|
|
14570
|
+
"maximum",
|
|
14571
|
+
"minLength",
|
|
14572
|
+
"maxLength"
|
|
14573
|
+
];
|
|
14574
|
+
const PRIMITIVES = /* @__PURE__ */ new Set([
|
|
14575
|
+
"string",
|
|
14576
|
+
"number",
|
|
14577
|
+
"integer",
|
|
14578
|
+
"boolean",
|
|
14579
|
+
"null"
|
|
14580
|
+
]);
|
|
14581
|
+
const SAFE = Number.MAX_SAFE_INTEGER;
|
|
14582
|
+
function isRaw(value) {
|
|
14583
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
14584
|
+
}
|
|
14585
|
+
function jsonTypeOf(value) {
|
|
14586
|
+
if (value === null) return "null";
|
|
14587
|
+
if (typeof value === "number") return Number.isInteger(value) ? "integer" : "number";
|
|
14588
|
+
if (typeof value === "boolean") return "boolean";
|
|
14589
|
+
return "string";
|
|
14590
|
+
}
|
|
14591
|
+
function project(node, where, options) {
|
|
14592
|
+
if (!isRaw(node)) throw new Error(`${where}: not a JSON Schema object`);
|
|
14593
|
+
const branches = node.anyOf ?? node.oneOf;
|
|
14594
|
+
if (Array.isArray(branches)) {
|
|
14595
|
+
const projected = branches.map((branch, index) => project(branch, `${where}|${index}`, options));
|
|
14596
|
+
const flattened = projected.every((b) => b.type !== void 0 && b.type !== "array" && b.type !== "object") ? primitiveUnion(projected, where, options) : flattenDiscriminatedUnion(projected, where);
|
|
14597
|
+
return typeof node.description === "string" ? {
|
|
14598
|
+
...flattened,
|
|
14599
|
+
description: node.description
|
|
14600
|
+
} : flattened;
|
|
14601
|
+
}
|
|
14602
|
+
if (node.allOf !== void 0 || node.$ref !== void 0 || node.not !== void 0) throw new Error(`${where}: allOf / $ref / not cannot be projected to the tool-schema subset`);
|
|
14603
|
+
const out = {};
|
|
14604
|
+
if (node.type !== void 0) {
|
|
14605
|
+
if (Array.isArray(node.type)) throw new Error(`${where}: type arrays are outside the tool-schema subset`);
|
|
14606
|
+
const type = String(node.type);
|
|
14607
|
+
if (type !== "object" && type !== "array" && !PRIMITIVES.has(type)) throw new Error(`${where}: unknown type ${type}`);
|
|
14608
|
+
out.type = type;
|
|
14609
|
+
}
|
|
14610
|
+
if (typeof node.description === "string") out.description = node.description;
|
|
14611
|
+
if (node.const !== void 0) {
|
|
14612
|
+
out.enum = [node.const];
|
|
14613
|
+
out.type ??= jsonTypeOf(node.const);
|
|
14614
|
+
}
|
|
14615
|
+
if (Array.isArray(node.enum)) out.enum = [...node.enum];
|
|
14616
|
+
if (isRaw(node.properties)) {
|
|
14617
|
+
out.properties = {};
|
|
14618
|
+
const required = Array.isArray(node.required) ? node.required : [];
|
|
14619
|
+
for (const [key, value] of Object.entries(node.properties)) {
|
|
14620
|
+
if (required.includes(key) && isNullable(value)) throw new Error(`${where}.${key}: a required nullable field has no tool-schema form; make it optional (omitted = null)`);
|
|
14621
|
+
out.properties[key] = project(value, `${where}.${key}`, options);
|
|
14622
|
+
}
|
|
14623
|
+
out.type ??= "object";
|
|
14624
|
+
}
|
|
14625
|
+
if (Array.isArray(node.required) && node.required.length > 0) out.required = [...node.required];
|
|
14626
|
+
if (node.items !== void 0) out.items = project(node.items, `${where}[]`, options);
|
|
14627
|
+
const integer = out.type === "integer";
|
|
14628
|
+
const lower = [typeof node.minimum === "number" ? node.minimum : void 0, typeof node.exclusiveMinimum === "number" ? node.exclusiveMinimum + (integer ? 1 : 0) : void 0].filter((v) => v !== void 0 && v > -SAFE);
|
|
14629
|
+
const upper = [typeof node.maximum === "number" ? node.maximum : void 0, typeof node.exclusiveMaximum === "number" ? node.exclusiveMaximum - (integer ? 1 : 0) : void 0].filter((v) => v !== void 0 && v < SAFE);
|
|
14630
|
+
if (lower.length > 0) out.minimum = Math.max(...lower);
|
|
14631
|
+
if (upper.length > 0) out.maximum = Math.min(...upper);
|
|
14632
|
+
if (typeof node.minLength === "number" && node.minLength > 0) out.minLength = node.minLength;
|
|
14633
|
+
if (typeof node.maxLength === "number") out.maxLength = node.maxLength;
|
|
14634
|
+
return out;
|
|
14635
|
+
}
|
|
14636
|
+
function isNullable(node) {
|
|
14637
|
+
if (!isRaw(node)) return false;
|
|
14638
|
+
const branches = node.anyOf ?? node.oneOf;
|
|
14639
|
+
return node.type === "null" || Array.isArray(branches) && branches.some((b) => isRaw(b) && b.type === "null");
|
|
14640
|
+
}
|
|
14641
|
+
/**
|
|
14642
|
+
* `T | null` → `T` (the field is optional, so omitting it is the JSON form of
|
|
14643
|
+
* null); any other union of primitives → the type the operation names in
|
|
14644
|
+
* `unionAs`. The branch constraints are dropped (looser).
|
|
14645
|
+
*/
|
|
14646
|
+
function primitiveUnion(branches, where, options) {
|
|
14647
|
+
const types = [...new Set(branches.map((b) => b.type).filter((t) => t !== "null"))];
|
|
14648
|
+
if (types.length === 1) return { type: types[0] };
|
|
14649
|
+
const chosen = options.unionAs[where];
|
|
14650
|
+
if (!chosen || !types.includes(chosen)) throw new Error(`${where}: a union of ${types.join(" | ")} needs unionAs (the one type to advertise)`);
|
|
14651
|
+
return { type: chosen };
|
|
14652
|
+
}
|
|
14653
|
+
/** The one property every object branch pins to a distinct single value. */
|
|
14654
|
+
function discriminatorOf(branches) {
|
|
14655
|
+
const first = branches[0];
|
|
14656
|
+
if (!first?.properties) return null;
|
|
14657
|
+
for (const key of Object.keys(first.properties)) {
|
|
14658
|
+
const values = branches.map((b) => b.properties?.[key]?.enum);
|
|
14659
|
+
if (values.every((v) => Array.isArray(v) && v.length === 1) && new Set(values.map((v) => v[0])).size === branches.length) return key;
|
|
14660
|
+
}
|
|
14661
|
+
return null;
|
|
14662
|
+
}
|
|
14663
|
+
function flattenDiscriminatedUnion(branches, where) {
|
|
14664
|
+
if (!branches.every((b) => b.type === "object")) throw new Error(`${where}: only unions of primitives or discriminated unions of objects can be projected`);
|
|
14665
|
+
const discriminator = discriminatorOf(branches);
|
|
14666
|
+
if (!discriminator) throw new Error(`${where}: a union of objects needs a discriminator property to be flattened`);
|
|
14667
|
+
const tags = branches.map((b) => b.properties[discriminator].enum[0]);
|
|
14668
|
+
const properties = { [discriminator]: {
|
|
14669
|
+
type: "string",
|
|
14670
|
+
enum: tags,
|
|
14671
|
+
description: `Which variant this is; the other fields apply per ${discriminator} as their descriptions say.`
|
|
14672
|
+
} };
|
|
14673
|
+
const order = [];
|
|
14674
|
+
const byKey = /* @__PURE__ */ new Map();
|
|
14675
|
+
branches.forEach((branch, index) => {
|
|
14676
|
+
for (const [key, schema] of Object.entries(branch.properties ?? {})) {
|
|
14677
|
+
if (key === discriminator) continue;
|
|
14678
|
+
if (!byKey.has(key)) {
|
|
14679
|
+
byKey.set(key, []);
|
|
14680
|
+
order.push(key);
|
|
14681
|
+
}
|
|
14682
|
+
byKey.get(key).push({
|
|
14683
|
+
tag: tags[index],
|
|
14684
|
+
schema
|
|
14685
|
+
});
|
|
14686
|
+
}
|
|
14687
|
+
});
|
|
14688
|
+
for (const key of order) {
|
|
14689
|
+
const uses = byKey.get(key);
|
|
14690
|
+
const merged = uses.map((u) => u.schema).reduce(loosest);
|
|
14691
|
+
const descriptions = [...new Set(uses.map((u) => u.schema.description).filter((d) => Boolean(d)))];
|
|
14692
|
+
const usedBy = uses.length === branches.length ? "" : `Only for ${discriminator} ${uses.map((u) => u.tag).join(", ")}.`;
|
|
14693
|
+
const description = [...descriptions, usedBy].filter(Boolean).join(" ");
|
|
14694
|
+
const { description: _drop, ...rest } = merged;
|
|
14695
|
+
properties[key] = description ? {
|
|
14696
|
+
...rest,
|
|
14697
|
+
description
|
|
14698
|
+
} : rest;
|
|
14699
|
+
}
|
|
14700
|
+
return {
|
|
14701
|
+
type: "object",
|
|
14702
|
+
properties,
|
|
14703
|
+
required: [discriminator]
|
|
14704
|
+
};
|
|
14705
|
+
}
|
|
14706
|
+
/** The loosest schema accepting everything either side accepts (within the subset). */
|
|
14707
|
+
function loosest(a, b) {
|
|
14708
|
+
if (JSON.stringify(a) === JSON.stringify(b)) return a;
|
|
14709
|
+
const out = {};
|
|
14710
|
+
if (a.type !== void 0 && a.type === b.type) out.type = a.type;
|
|
14711
|
+
if (a.enum && b.enum) out.enum = [.../* @__PURE__ */ new Set([...a.enum, ...b.enum])];
|
|
14712
|
+
if (a.minimum !== void 0 && b.minimum !== void 0) out.minimum = Math.min(a.minimum, b.minimum);
|
|
14713
|
+
if (a.maximum !== void 0 && b.maximum !== void 0) out.maximum = Math.max(a.maximum, b.maximum);
|
|
14714
|
+
if (a.minLength !== void 0 && b.minLength !== void 0) out.minLength = Math.min(a.minLength, b.minLength);
|
|
14715
|
+
if (a.maxLength !== void 0 && b.maxLength !== void 0) out.maxLength = Math.max(a.maxLength, b.maxLength);
|
|
14716
|
+
if (a.items && b.items) out.items = loosest(a.items, b.items);
|
|
14717
|
+
if (a.properties && b.properties) {
|
|
14718
|
+
out.properties = {};
|
|
14719
|
+
for (const key of /* @__PURE__ */ new Set([...Object.keys(a.properties), ...Object.keys(b.properties)])) {
|
|
14720
|
+
const pa = a.properties[key];
|
|
14721
|
+
const pb = b.properties[key];
|
|
14722
|
+
out.properties[key] = pa && pb ? loosest(pa, pb) : pa ?? pb;
|
|
14723
|
+
}
|
|
14724
|
+
const required = (a.required ?? []).filter((key) => (b.required ?? []).includes(key));
|
|
14725
|
+
if (required.length > 0) out.required = required;
|
|
14726
|
+
}
|
|
14727
|
+
if (a.description) out.description = a.description;
|
|
14728
|
+
return out;
|
|
14729
|
+
}
|
|
14730
|
+
/** The tool-schema projection of an operation's zod request schema (input side). */
|
|
14731
|
+
function toRaftToolInputSchema(schema, options = {}) {
|
|
14732
|
+
const projected = project(toJSONSchema(schema, {
|
|
14733
|
+
io: "input",
|
|
14734
|
+
target: "draft-2020-12",
|
|
14735
|
+
reused: "inline",
|
|
14736
|
+
unrepresentable: "throw"
|
|
14737
|
+
}), "input", { unionAs: Object.fromEntries(Object.entries(options.unionAs ?? {}).map(([key, type]) => [`input.${key}`, type])) });
|
|
14738
|
+
if (projected.type !== "object") throw new Error("an operation's input schema must be an object");
|
|
14739
|
+
for (const key of options.omit ?? []) {
|
|
14740
|
+
if (projected.properties) delete projected.properties[key];
|
|
14741
|
+
if (projected.required) projected.required = projected.required.filter((r) => r !== key);
|
|
14742
|
+
}
|
|
14743
|
+
for (const [key, description] of Object.entries(options.describe ?? {})) {
|
|
14744
|
+
const property = projected.properties?.[key];
|
|
14745
|
+
if (!property) throw new Error(`cannot describe unknown field ${key}`);
|
|
14746
|
+
property.description = description;
|
|
14747
|
+
}
|
|
14748
|
+
if (projected.required?.length === 0) delete projected.required;
|
|
14749
|
+
projected.properties ??= {};
|
|
14750
|
+
return projected;
|
|
14751
|
+
}
|
|
14752
|
+
//#endregion
|
|
14753
|
+
//#region src/operations.ts
|
|
14754
|
+
const nothing = {
|
|
14755
|
+
model: [],
|
|
14756
|
+
code: []
|
|
14757
|
+
};
|
|
14758
|
+
const small = {
|
|
14759
|
+
mayBeLarge: false,
|
|
14760
|
+
boundBy: []
|
|
14761
|
+
};
|
|
14762
|
+
const OPERATION_DEFS = [
|
|
14763
|
+
{
|
|
14764
|
+
name: "identity.whoami",
|
|
14765
|
+
description: "Who this credential is: your agent, the server, the capabilities granted to the credential, and the operating guide for External Agents.",
|
|
14766
|
+
schema: whoamiRequestSchema,
|
|
14767
|
+
routes: ["agentContext"],
|
|
14768
|
+
consumes: nothing,
|
|
14769
|
+
output: {
|
|
14770
|
+
mayBeLarge: true,
|
|
14771
|
+
boundBy: []
|
|
14772
|
+
}
|
|
14773
|
+
},
|
|
14774
|
+
{
|
|
14775
|
+
name: "inbox.check",
|
|
14776
|
+
description: "Pull one batch of new messages addressed to you (DMs, @mentions, followed threads, joined channels). Handle them, then call inbox.commit; the next check acknowledges the committed batch.",
|
|
14777
|
+
schema: checkInboxRequestSchema,
|
|
14778
|
+
routes: ["events"],
|
|
14779
|
+
consumes: {
|
|
14780
|
+
model: ["inbox", "seen"],
|
|
14781
|
+
code: "refused"
|
|
14782
|
+
},
|
|
14783
|
+
output: {
|
|
14784
|
+
mayBeLarge: true,
|
|
14785
|
+
boundBy: ["limit"]
|
|
14786
|
+
}
|
|
14787
|
+
},
|
|
14788
|
+
{
|
|
14789
|
+
name: "inbox.drain",
|
|
14790
|
+
description: "Pull new messages until the server reports nothing more, returned together. Each batch is acknowledged by the pull after it; call inbox.commit after handling the result.",
|
|
14791
|
+
schema: drainInboxRequestSchema,
|
|
14792
|
+
routes: ["events"],
|
|
14793
|
+
consumes: {
|
|
14794
|
+
model: ["inbox", "seen"],
|
|
14795
|
+
code: "refused"
|
|
14796
|
+
},
|
|
14797
|
+
output: {
|
|
14798
|
+
mayBeLarge: true,
|
|
14799
|
+
boundBy: ["limit"]
|
|
14800
|
+
}
|
|
14801
|
+
},
|
|
14802
|
+
{
|
|
14803
|
+
name: "inbox.commit",
|
|
14804
|
+
description: "Mark the last pulled inbox batch as handled (or the batch with the given cursor); the next inbox check acknowledges it on the server. Call it only after the messages were handled.",
|
|
14805
|
+
schema: commitInboxRequestSchema,
|
|
14806
|
+
routes: [],
|
|
14807
|
+
local: {
|
|
14808
|
+
sideEffect: "write",
|
|
14809
|
+
idempotency: "natural"
|
|
14810
|
+
},
|
|
14811
|
+
consumes: {
|
|
14812
|
+
model: ["inbox"],
|
|
14813
|
+
code: "refused"
|
|
14814
|
+
},
|
|
14815
|
+
output: small
|
|
14816
|
+
},
|
|
14817
|
+
{
|
|
14818
|
+
name: "inbox.list",
|
|
14819
|
+
description: "List conversations with unread messages, newest activity first, each with where to start reading. Consumes nothing.",
|
|
14820
|
+
schema: listInboxRequestSchema,
|
|
14821
|
+
routes: ["inboxList"],
|
|
14822
|
+
consumes: nothing,
|
|
14823
|
+
output: {
|
|
14824
|
+
mayBeLarge: true,
|
|
14825
|
+
boundBy: ["before", "limit"]
|
|
14826
|
+
}
|
|
14827
|
+
},
|
|
14828
|
+
{
|
|
14829
|
+
name: "messages.read",
|
|
14830
|
+
description: "Read a conversation (channel, DM, or thread): the newest messages, or a window after / before / around a position. Marks it read and counts the messages as seen, so a reply into it is not held.",
|
|
14831
|
+
schema: readHistoryRequestSchema,
|
|
14832
|
+
unionAs: { around: "string" },
|
|
14833
|
+
routes: ["historyRead"],
|
|
14834
|
+
consumes: {
|
|
14835
|
+
model: ["read_cursor", "seen"],
|
|
14836
|
+
code: []
|
|
14837
|
+
},
|
|
14838
|
+
output: {
|
|
14839
|
+
mayBeLarge: true,
|
|
14840
|
+
boundBy: [
|
|
14841
|
+
"after",
|
|
14842
|
+
"before",
|
|
14843
|
+
"around",
|
|
14844
|
+
"limit"
|
|
14845
|
+
]
|
|
14846
|
+
}
|
|
14847
|
+
},
|
|
14848
|
+
{
|
|
14849
|
+
name: "messages.send",
|
|
14850
|
+
description: "Send a message to a channel, DM, or thread. If newer messages arrived there that you have not seen, the send is held and returns them: read them, then send again with the same idempotencyKey to go ahead, or drop it.",
|
|
14851
|
+
schema: sendMessageRequestSchema,
|
|
14852
|
+
hiddenFields: ["seen"],
|
|
14853
|
+
routes: ["messageSendV2"],
|
|
14854
|
+
idempotencyArg: "idempotencyKey",
|
|
14855
|
+
mayInterrupt: true,
|
|
14856
|
+
consumes: nothing,
|
|
14857
|
+
output: small
|
|
14858
|
+
},
|
|
14859
|
+
{
|
|
14860
|
+
name: "messages.reply",
|
|
14861
|
+
description: "Reply where a received message came from (its conversation or thread). Held like messages.send when newer messages arrived there.",
|
|
14862
|
+
schema: replyToRequestSchema,
|
|
14863
|
+
hiddenFields: ["seen"],
|
|
14864
|
+
routes: ["messageSendV2"],
|
|
14865
|
+
idempotencyArg: "idempotencyKey",
|
|
14866
|
+
mayInterrupt: true,
|
|
14867
|
+
consumes: nothing,
|
|
14868
|
+
output: small
|
|
14869
|
+
},
|
|
14870
|
+
{
|
|
14871
|
+
name: "messages.search",
|
|
14872
|
+
description: "Search messages by text, conversation, sender, or time; results are previews. Use it to find a specific message, not to check what is new.",
|
|
14873
|
+
schema: searchMessagesRequestSchema,
|
|
14874
|
+
routes: ["messageSearch"],
|
|
14875
|
+
consumes: nothing,
|
|
14876
|
+
output: {
|
|
14877
|
+
mayBeLarge: true,
|
|
14878
|
+
boundBy: ["limit", "offset"]
|
|
14879
|
+
}
|
|
14880
|
+
},
|
|
14881
|
+
{
|
|
14882
|
+
name: "messages.resolve",
|
|
14883
|
+
description: "Resolve a message id to the full message and the conversation it belongs to.",
|
|
14884
|
+
schema: resolveMessageRequestSchema,
|
|
14885
|
+
routes: ["messageResolve"],
|
|
14886
|
+
consumes: nothing,
|
|
14887
|
+
output: small
|
|
14888
|
+
},
|
|
14889
|
+
{
|
|
14890
|
+
name: "messages.react",
|
|
14891
|
+
description: "Add an emoji reaction to a message.",
|
|
14892
|
+
schema: reactRequestSchema,
|
|
14893
|
+
routes: ["messageReactionAdd"],
|
|
14894
|
+
consumes: nothing,
|
|
14895
|
+
output: small
|
|
14896
|
+
},
|
|
14897
|
+
{
|
|
14898
|
+
name: "messages.unreact",
|
|
14899
|
+
description: "Remove your emoji reaction from a message.",
|
|
14900
|
+
schema: reactRequestSchema,
|
|
14901
|
+
routes: ["messageReactionRemove"],
|
|
14902
|
+
consumes: nothing,
|
|
14903
|
+
output: small
|
|
14904
|
+
},
|
|
14905
|
+
{
|
|
14906
|
+
name: "attachments.comments",
|
|
14907
|
+
description: "List the comments on an attachment.",
|
|
14908
|
+
schema: attachmentCommentsRequestSchema,
|
|
14909
|
+
routes: ["attachmentCommentsList"],
|
|
14910
|
+
consumes: nothing,
|
|
14911
|
+
output: {
|
|
14912
|
+
mayBeLarge: true,
|
|
14913
|
+
boundBy: ["limit"]
|
|
14914
|
+
}
|
|
14915
|
+
},
|
|
14916
|
+
{
|
|
14917
|
+
name: "mentions.pending",
|
|
14918
|
+
description: "List @mentions you sent that reached nobody, each with the actions that would deliver it.",
|
|
14919
|
+
schema: pendingMentionActionsRequestSchema,
|
|
14920
|
+
routes: ["mentionActionsPending"],
|
|
14921
|
+
consumes: nothing,
|
|
14922
|
+
output: {
|
|
14923
|
+
mayBeLarge: true,
|
|
14924
|
+
boundBy: ["limit"]
|
|
14925
|
+
}
|
|
14926
|
+
},
|
|
14927
|
+
{
|
|
14928
|
+
name: "mentions.execute",
|
|
14929
|
+
description: "Deliver unreached @mentions: notify the mentioned target, or add them to the conversation.",
|
|
14930
|
+
schema: executeMentionActionRequestSchema,
|
|
14931
|
+
routes: ["mentionActionsExecute"],
|
|
14932
|
+
consumes: nothing,
|
|
14933
|
+
output: small
|
|
14934
|
+
},
|
|
14935
|
+
{
|
|
14936
|
+
name: "mentions.deliveries",
|
|
14937
|
+
description: "Whether each @mention in a message you sent reached its target.",
|
|
14938
|
+
schema: senderMentionDeliveriesRequestSchema,
|
|
14939
|
+
routes: ["senderMentionDeliveries"],
|
|
14940
|
+
consumes: nothing,
|
|
14941
|
+
output: small
|
|
14942
|
+
},
|
|
14943
|
+
{
|
|
14944
|
+
name: "actions.prepare",
|
|
14945
|
+
description: "Post an action card (create a channel, add members, create an agent, or an integration step) for a human to confirm; the human who clicks it carries it out as themselves. When it is executed (or fails), the outcome arrives as a reply that @mentions you in the card's thread (or in the thread the card was posted in).",
|
|
14946
|
+
schema: prepareActionCardRequestSchema,
|
|
14947
|
+
fieldDescriptions: {
|
|
14948
|
+
target: "Conversation to post the card in: `#channel`, `dm:@peer`, or a thread.",
|
|
14949
|
+
action: "The operation the card proposes: `type` picks it, and the other fields apply per type as described."
|
|
14950
|
+
},
|
|
14951
|
+
routes: ["actionPrepare"],
|
|
14952
|
+
consumes: nothing,
|
|
14953
|
+
output: small
|
|
14954
|
+
},
|
|
14955
|
+
{
|
|
14956
|
+
name: "manual.get",
|
|
14957
|
+
description: "Fetch a topic of the Raft Manual for Agents. intent and reason are required and must never carry prompts, credentials, or message text.",
|
|
14958
|
+
schema: getManualTopicRequestSchema,
|
|
14959
|
+
routes: ["knowledgeGet"],
|
|
14960
|
+
consumes: nothing,
|
|
14961
|
+
output: {
|
|
14962
|
+
mayBeLarge: true,
|
|
14963
|
+
boundBy: []
|
|
14964
|
+
}
|
|
14965
|
+
},
|
|
14966
|
+
{
|
|
14967
|
+
name: "manual.search",
|
|
14968
|
+
description: "Search the Raft Manual for Agents by keywords; returns topic ids to fetch with manual.get.",
|
|
14969
|
+
schema: searchManualRequestSchema,
|
|
14970
|
+
routes: ["knowledgeSearch"],
|
|
14971
|
+
consumes: nothing,
|
|
14972
|
+
output: {
|
|
14973
|
+
mayBeLarge: true,
|
|
14974
|
+
boundBy: []
|
|
14975
|
+
}
|
|
14976
|
+
},
|
|
14977
|
+
{
|
|
14978
|
+
name: "tasks.claim",
|
|
14979
|
+
description: "Claim tasks (by number, or a top-level message to claim as a task) before working on them. Each row says whether you may work on it; held like a send when the channel has messages you have not seen.",
|
|
14980
|
+
schema: claimTasksRequestSchema,
|
|
14981
|
+
routes: ["taskClaim"],
|
|
14982
|
+
mayInterrupt: true,
|
|
14983
|
+
consumes: nothing,
|
|
14984
|
+
output: small
|
|
14985
|
+
},
|
|
14986
|
+
{
|
|
14987
|
+
name: "tasks.list",
|
|
14988
|
+
description: "A channel's task board, or your own tasks across channels with mine: true.",
|
|
14989
|
+
schema: listTasksRequestSchema,
|
|
14990
|
+
routes: ["taskList"],
|
|
14991
|
+
consumes: nothing,
|
|
14992
|
+
output: {
|
|
14993
|
+
mayBeLarge: true,
|
|
14994
|
+
boundBy: ["status"]
|
|
14995
|
+
}
|
|
14996
|
+
},
|
|
14997
|
+
{
|
|
14998
|
+
name: "tasks.create",
|
|
14999
|
+
description: "Create one or more tasks on a channel's board; each gets its own thread.",
|
|
15000
|
+
schema: createTasksRequestSchema,
|
|
15001
|
+
routes: ["taskCreate"],
|
|
15002
|
+
consumes: nothing,
|
|
15003
|
+
output: small
|
|
15004
|
+
},
|
|
15005
|
+
{
|
|
15006
|
+
name: "tasks.unclaim",
|
|
15007
|
+
description: "Release your claim on a task so someone else can take it.",
|
|
15008
|
+
schema: taskRefSchema,
|
|
15009
|
+
routes: ["taskUnclaim"],
|
|
15010
|
+
consumes: nothing,
|
|
15011
|
+
output: small
|
|
15012
|
+
},
|
|
15013
|
+
{
|
|
15014
|
+
name: "tasks.assign",
|
|
15015
|
+
description: "Assign a task to someone (yourself, or anyone if you are an owner/admin). To clear the assignee use tasks.unassign.",
|
|
15016
|
+
schema: assignTaskRequestSchema,
|
|
15017
|
+
routes: ["taskAssign"],
|
|
15018
|
+
consumes: nothing,
|
|
15019
|
+
output: small
|
|
15020
|
+
},
|
|
15021
|
+
{
|
|
15022
|
+
name: "tasks.unassign",
|
|
15023
|
+
description: "Clear a task's assignee.",
|
|
15024
|
+
schema: unassignTaskRequestSchema,
|
|
15025
|
+
routes: ["taskAssign"],
|
|
15026
|
+
consumes: nothing,
|
|
15027
|
+
output: small
|
|
15028
|
+
},
|
|
15029
|
+
{
|
|
15030
|
+
name: "tasks.updateStatus",
|
|
15031
|
+
description: "Move a task: todo → in_progress → in_review → done, or closed from anywhere. Held like a send when the channel has messages you have not seen.",
|
|
15032
|
+
schema: updateTaskStatusRequestSchema,
|
|
15033
|
+
routes: ["taskUpdateStatus"],
|
|
15034
|
+
mayInterrupt: true,
|
|
15035
|
+
consumes: nothing,
|
|
15036
|
+
output: small
|
|
15037
|
+
},
|
|
15038
|
+
{
|
|
15039
|
+
name: "tasks.amend",
|
|
15040
|
+
description: "Change a task's title or description. Held like a send when the channel has messages you have not seen.",
|
|
15041
|
+
schema: amendTaskRequestSchema,
|
|
15042
|
+
routes: ["taskAmend"],
|
|
15043
|
+
mayInterrupt: true,
|
|
15044
|
+
consumes: nothing,
|
|
15045
|
+
output: small
|
|
15046
|
+
},
|
|
15047
|
+
{
|
|
15048
|
+
name: "tasks.history",
|
|
15049
|
+
description: "A task's history: status changes, claims, and assignments.",
|
|
15050
|
+
schema: taskRefSchema,
|
|
15051
|
+
routes: ["taskHistory"],
|
|
15052
|
+
consumes: nothing,
|
|
15053
|
+
output: {
|
|
15054
|
+
mayBeLarge: true,
|
|
15055
|
+
boundBy: []
|
|
15056
|
+
}
|
|
15057
|
+
},
|
|
15058
|
+
{
|
|
15059
|
+
name: "tasks.convert",
|
|
15060
|
+
description: "Turn a top-level message into an unassigned task.",
|
|
15061
|
+
schema: convertMessageToTaskRequestSchema,
|
|
15062
|
+
routes: ["taskConvert"],
|
|
15063
|
+
consumes: nothing,
|
|
15064
|
+
output: small
|
|
15065
|
+
},
|
|
15066
|
+
{
|
|
15067
|
+
name: "tasks.delete",
|
|
15068
|
+
description: "Delete a task.",
|
|
15069
|
+
schema: taskRefSchema,
|
|
15070
|
+
routes: ["taskDelete"],
|
|
15071
|
+
consumes: nothing,
|
|
15072
|
+
output: small
|
|
15073
|
+
},
|
|
15074
|
+
{
|
|
15075
|
+
name: "channels.join",
|
|
15076
|
+
description: "Join a public channel. Joining is explicit; sending never joins.",
|
|
15077
|
+
schema: channelTargetRequestSchema,
|
|
15078
|
+
routes: ["serverInfo", "channelJoin"],
|
|
15079
|
+
consumes: nothing,
|
|
15080
|
+
output: small
|
|
15081
|
+
},
|
|
15082
|
+
{
|
|
15083
|
+
name: "channels.leave",
|
|
15084
|
+
description: "Leave a channel; ordinary channel messages stop arriving.",
|
|
15085
|
+
schema: channelTargetRequestSchema,
|
|
15086
|
+
routes: ["serverInfo", "channelLeave"],
|
|
15087
|
+
consumes: nothing,
|
|
15088
|
+
output: small
|
|
15089
|
+
},
|
|
15090
|
+
{
|
|
15091
|
+
name: "channels.mute",
|
|
15092
|
+
description: "Mute a channel's ordinary activity; @mentions, DMs, and followed threads still arrive.",
|
|
15093
|
+
schema: channelTargetRequestSchema,
|
|
15094
|
+
routes: ["serverInfo", "channelMute"],
|
|
15095
|
+
consumes: nothing,
|
|
15096
|
+
output: small
|
|
15097
|
+
},
|
|
15098
|
+
{
|
|
15099
|
+
name: "channels.unmute",
|
|
15100
|
+
description: "Unmute a channel's ordinary activity.",
|
|
15101
|
+
schema: channelTargetRequestSchema,
|
|
15102
|
+
routes: ["serverInfo", "channelUnmute"],
|
|
15103
|
+
consumes: nothing,
|
|
15104
|
+
output: small
|
|
15105
|
+
},
|
|
15106
|
+
{
|
|
15107
|
+
name: "channels.members",
|
|
15108
|
+
description: "Who is in a channel, DM, or thread.",
|
|
15109
|
+
schema: channelMembersRequestSchema,
|
|
15110
|
+
routes: ["channelMembers"],
|
|
15111
|
+
consumes: nothing,
|
|
15112
|
+
output: {
|
|
15113
|
+
mayBeLarge: true,
|
|
15114
|
+
boundBy: []
|
|
15115
|
+
}
|
|
15116
|
+
},
|
|
15117
|
+
{
|
|
15118
|
+
name: "threads.list",
|
|
15119
|
+
description: "The threads you follow.",
|
|
15120
|
+
schema: listThreadsRequestSchema,
|
|
15121
|
+
routes: ["threadList"],
|
|
15122
|
+
consumes: nothing,
|
|
15123
|
+
output: {
|
|
15124
|
+
mayBeLarge: true,
|
|
15125
|
+
boundBy: []
|
|
15126
|
+
}
|
|
15127
|
+
},
|
|
15128
|
+
{
|
|
15129
|
+
name: "threads.unfollow",
|
|
15130
|
+
description: "Stop following a thread; a direct @mention follows it again.",
|
|
15131
|
+
schema: unfollowThreadRequestSchema,
|
|
15132
|
+
routes: ["threadUnfollow"],
|
|
15133
|
+
consumes: nothing,
|
|
15134
|
+
output: small
|
|
15135
|
+
},
|
|
15136
|
+
{
|
|
15137
|
+
name: "server.info",
|
|
15138
|
+
description: "The server you are on: a summary by default, or one paged section (channels, agents, humans), or the full overview.",
|
|
15139
|
+
schema: serverInfoRequestSchema,
|
|
15140
|
+
routes: ["serverInfo"],
|
|
15141
|
+
consumes: nothing,
|
|
15142
|
+
output: {
|
|
15143
|
+
mayBeLarge: true,
|
|
15144
|
+
boundBy: [
|
|
15145
|
+
"view",
|
|
15146
|
+
"offset",
|
|
15147
|
+
"limit"
|
|
15148
|
+
]
|
|
15149
|
+
}
|
|
15150
|
+
},
|
|
15151
|
+
{
|
|
15152
|
+
name: "profile.show",
|
|
15153
|
+
description: "Show your profile, or someone else's by @handle.",
|
|
15154
|
+
schema: showProfileRequestSchema,
|
|
15155
|
+
routes: ["profileShow"],
|
|
15156
|
+
consumes: nothing,
|
|
15157
|
+
output: small
|
|
15158
|
+
},
|
|
15159
|
+
{
|
|
15160
|
+
name: "profile.update",
|
|
15161
|
+
description: "Update your display name, description, or avatar URL.",
|
|
15162
|
+
schema: updateProfileRequestSchema,
|
|
15163
|
+
fieldDescriptions: {
|
|
15164
|
+
displayName: "New display name.",
|
|
15165
|
+
description: "New profile description.",
|
|
15166
|
+
avatarUrl: "URL of the new avatar image."
|
|
15167
|
+
},
|
|
15168
|
+
routes: ["profileUpdate"],
|
|
15169
|
+
consumes: nothing,
|
|
15170
|
+
output: small
|
|
15171
|
+
}
|
|
15172
|
+
];
|
|
15173
|
+
/** `tasks.updateStatus` → `tasks_update_status`. */
|
|
15174
|
+
function raftToolNameFor(name) {
|
|
15175
|
+
return name.replace(/\./g, "_").replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase();
|
|
15176
|
+
}
|
|
15177
|
+
const IDEMPOTENCY_STRENGTH = {
|
|
15178
|
+
natural: 0,
|
|
15179
|
+
key: 1,
|
|
15180
|
+
none: 2
|
|
15181
|
+
};
|
|
15182
|
+
function deriveSpec(def) {
|
|
15183
|
+
const metas = def.routes.map((route) => AGENT_API_ROUTE_META[route]);
|
|
15184
|
+
const sideEffect = def.local ? def.local.sideEffect : metas.every((meta) => meta.sideEffect === "read") ? "read" : "write";
|
|
15185
|
+
const idempotencyKind = def.local ? def.local.idempotency : metas.map((meta) => meta.idempotency).reduce((a, b) => IDEMPOTENCY_STRENGTH[b] > IDEMPOTENCY_STRENGTH[a] ? b : a, "natural");
|
|
15186
|
+
if (idempotencyKind === "key" && !def.idempotencyArg) throw new Error(`${def.name}: keyed routes need idempotencyArg`);
|
|
15187
|
+
const capability = [...new Set(def.routes.map((route) => agentApiContract[route].capability))].sort();
|
|
15188
|
+
return {
|
|
15189
|
+
name: def.name,
|
|
15190
|
+
toolName: def.toolName ?? raftToolNameFor(def.name),
|
|
15191
|
+
description: def.description,
|
|
15192
|
+
inputSchema: toRaftToolInputSchema(def.schema, {
|
|
15193
|
+
omit: def.hiddenFields,
|
|
15194
|
+
describe: def.fieldDescriptions,
|
|
15195
|
+
unionAs: def.unionAs
|
|
15196
|
+
}),
|
|
15197
|
+
sideEffect,
|
|
15198
|
+
idempotency: idempotencyKind === "key" ? {
|
|
15199
|
+
kind: "key",
|
|
15200
|
+
arg: def.idempotencyArg
|
|
15201
|
+
} : { kind: idempotencyKind },
|
|
15202
|
+
capability,
|
|
15203
|
+
modelOnly: def.consumes.code === "refused",
|
|
15204
|
+
mayInterrupt: def.mayInterrupt === true,
|
|
15205
|
+
consumes: {
|
|
15206
|
+
model: [...def.consumes.model],
|
|
15207
|
+
code: def.consumes.code === "refused" ? "refused" : [...def.consumes.code]
|
|
15208
|
+
},
|
|
15209
|
+
output: {
|
|
15210
|
+
mayBeLarge: def.output.mayBeLarge,
|
|
15211
|
+
boundBy: [...def.output.boundBy]
|
|
15212
|
+
},
|
|
15213
|
+
...def.deprecated ? { deprecated: true } : {}
|
|
15214
|
+
};
|
|
15215
|
+
}
|
|
15216
|
+
function fnv1a64(text) {
|
|
15217
|
+
let hash = 14695981039346656037n;
|
|
15218
|
+
const prime = 1099511628211n;
|
|
15219
|
+
const mask = 18446744073709551615n;
|
|
15220
|
+
for (let index = 0; index < text.length; index += 1) {
|
|
15221
|
+
hash ^= BigInt(text.charCodeAt(index));
|
|
15222
|
+
hash = hash * prime & mask;
|
|
15223
|
+
}
|
|
15224
|
+
return hash.toString(16).padStart(16, "0");
|
|
15225
|
+
}
|
|
15226
|
+
/** Every agent operation on `createRaft`, in a stable order. */
|
|
15227
|
+
const RAFT_OPERATIONS = Object.freeze(OPERATION_DEFS.map((def) => deriveSpec(def)));
|
|
15228
|
+
/** Content hash of `RAFT_OPERATIONS` (16 hex digits); changes whenever any field of any operation changes. */
|
|
15229
|
+
const RAFT_OPERATIONS_VERSION = fnv1a64(JSON.stringify(RAFT_OPERATIONS));
|
|
15230
|
+
const RAFT_OPERATIONS_SCHEMA = "raft-sdk-operations.v1";
|
|
15231
|
+
const DEFS_BY_NAME = new Map(OPERATION_DEFS.map((def) => [def.name, def]));
|
|
15232
|
+
const SPECS_BY_NAME = new Map(RAFT_OPERATIONS.map((spec) => [spec.name, spec]));
|
|
15233
|
+
/** The manifest entry and the zod request schema for a name, or undefined. */
|
|
15234
|
+
function lookupRaftOperation(name) {
|
|
15235
|
+
const def = DEFS_BY_NAME.get(name);
|
|
15236
|
+
const spec = SPECS_BY_NAME.get(name);
|
|
15237
|
+
return def && spec ? {
|
|
15238
|
+
spec,
|
|
15239
|
+
schema: def.schema
|
|
15240
|
+
} : void 0;
|
|
15241
|
+
}
|
|
15242
|
+
//#endregion
|
|
14045
15243
|
//#region src/raft.ts
|
|
14046
15244
|
function createRaft(options) {
|
|
14047
15245
|
const serverUrl = requireServerUrl(options.serverUrl);
|
|
@@ -14081,13 +15279,13 @@ function createRaft(options) {
|
|
|
14081
15279
|
* (same target, content, attachments) reuses the held send's idempotency key;
|
|
14082
15280
|
* a hold is remembered; a successful send clears it.
|
|
14083
15281
|
*/
|
|
14084
|
-
const sendWithState = (request) => withState(async () => {
|
|
15282
|
+
const sendWithState = (request, seen) => withState(async () => {
|
|
14085
15283
|
const contentHash = await hashRaftSendContent(request.target, request.content ?? "", request.attachmentIds ?? []);
|
|
14086
15284
|
const pending = request.idempotencyKey ? void 0 : session.findContinuation(request.target, contentHash);
|
|
14087
15285
|
const outcome = await sendMessage(api, pending ? {
|
|
14088
15286
|
...request,
|
|
14089
15287
|
idempotencyKey: pending.idempotencyKey
|
|
14090
|
-
} : request,
|
|
15288
|
+
} : request, seen);
|
|
14091
15289
|
if (outcome.ok && outcome.state === "interrupted" && outcome.interrupt.resume.idempotencyKey) session.rememberContinuation({
|
|
14092
15290
|
target: request.target,
|
|
14093
15291
|
idempotencyKey: outcome.interrupt.resume.idempotencyKey,
|
|
@@ -14097,7 +15295,47 @@ function createRaft(options) {
|
|
|
14097
15295
|
else if (outcome.ok && outcome.state === "sent") session.forgetContinuation(request.target, contentHash);
|
|
14098
15296
|
return outcome;
|
|
14099
15297
|
});
|
|
14100
|
-
|
|
15298
|
+
const checkWithState = (request, seen) => withState(async () => {
|
|
15299
|
+
const since = request.since ?? session.cursor ?? void 0;
|
|
15300
|
+
const outcome = await checkInbox(inboxApi, {
|
|
15301
|
+
...request,
|
|
15302
|
+
...since === void 0 ? {} : { since }
|
|
15303
|
+
}, seen);
|
|
15304
|
+
if (outcome.ok) {
|
|
15305
|
+
session.markDirty();
|
|
15306
|
+
if (since !== void 0 && (request.ack ?? "cursor") === "cursor") session.commit(since);
|
|
15307
|
+
if (outcome.data.ackMode === "cursor" && outcome.data.messages.length > 0) session.setPending(outcome.data.cursor);
|
|
15308
|
+
}
|
|
15309
|
+
return outcome;
|
|
15310
|
+
});
|
|
15311
|
+
const drainWithState = (request, seen) => (async function* () {
|
|
15312
|
+
await session.ensureLoaded();
|
|
15313
|
+
const since = request.since ?? session.cursor ?? void 0;
|
|
15314
|
+
return yield* drainInbox(inboxApi, {
|
|
15315
|
+
...request,
|
|
15316
|
+
...since === void 0 ? {} : { since }
|
|
15317
|
+
}, seen, async (sent, batch) => {
|
|
15318
|
+
if (sent !== null) session.commit(sent);
|
|
15319
|
+
if (batch.ackMode === "cursor" && batch.messages.length > 0) session.setPending(batch.cursor);
|
|
15320
|
+
session.markDirty();
|
|
15321
|
+
await session.save();
|
|
15322
|
+
});
|
|
15323
|
+
})();
|
|
15324
|
+
const commitInbox = async (target) => {
|
|
15325
|
+
await session.ensureLoaded();
|
|
15326
|
+
const cursor = session.commit(target === void 0 ? void 0 : target.cursor);
|
|
15327
|
+
return {
|
|
15328
|
+
cursor,
|
|
15329
|
+
saved: cursor === null ? true : await session.save()
|
|
15330
|
+
};
|
|
15331
|
+
};
|
|
15332
|
+
/** `seen` undefined: read without recording anything (a code read). */
|
|
15333
|
+
const readWithState = (request, seen) => withState(async () => {
|
|
15334
|
+
const outcome = await readHistory(api, request, seen);
|
|
15335
|
+
if (outcome.ok && seen) session.markDirty();
|
|
15336
|
+
return outcome;
|
|
15337
|
+
});
|
|
15338
|
+
const raft = {
|
|
14101
15339
|
identity: { whoami: () => getRaftContext(api) },
|
|
14102
15340
|
wake: {
|
|
14103
15341
|
verifyNotice: (input) => verifyInboxNotice(input),
|
|
@@ -14108,53 +15346,18 @@ function createRaft(options) {
|
|
|
14108
15346
|
}
|
|
14109
15347
|
},
|
|
14110
15348
|
inbox: {
|
|
14111
|
-
check: (request = {}) =>
|
|
14112
|
-
|
|
14113
|
-
|
|
14114
|
-
...request,
|
|
14115
|
-
...since === void 0 ? {} : { since }
|
|
14116
|
-
}, frontier);
|
|
14117
|
-
if (outcome.ok) {
|
|
14118
|
-
session.markDirty();
|
|
14119
|
-
if (since !== void 0 && (request.ack ?? "cursor") === "cursor") session.commit(since);
|
|
14120
|
-
if (outcome.data.ackMode === "cursor" && outcome.data.messages.length > 0) session.setPending(outcome.data.cursor);
|
|
14121
|
-
}
|
|
14122
|
-
return outcome;
|
|
14123
|
-
}),
|
|
14124
|
-
commit: async (target) => {
|
|
14125
|
-
await session.ensureLoaded();
|
|
14126
|
-
const cursor = session.commit(target === void 0 ? void 0 : target.cursor);
|
|
14127
|
-
return {
|
|
14128
|
-
cursor,
|
|
14129
|
-
saved: cursor === null ? true : await session.save()
|
|
14130
|
-
};
|
|
14131
|
-
},
|
|
14132
|
-
drain: (request = {}) => (async function* () {
|
|
14133
|
-
await session.ensureLoaded();
|
|
14134
|
-
const since = request.since ?? session.cursor ?? void 0;
|
|
14135
|
-
return yield* drainInbox(inboxApi, {
|
|
14136
|
-
...request,
|
|
14137
|
-
...since === void 0 ? {} : { since }
|
|
14138
|
-
}, frontier, async (sent, batch) => {
|
|
14139
|
-
if (sent !== null) session.commit(sent);
|
|
14140
|
-
if (batch.ackMode === "cursor" && batch.messages.length > 0) session.setPending(batch.cursor);
|
|
14141
|
-
session.markDirty();
|
|
14142
|
-
await session.save();
|
|
14143
|
-
});
|
|
14144
|
-
})(),
|
|
15349
|
+
check: (request = {}) => checkWithState(request, frontier),
|
|
15350
|
+
commit: (target) => commitInbox(target),
|
|
15351
|
+
drain: (request = {}) => drainWithState(request, frontier),
|
|
14145
15352
|
list: (request) => listInbox(api, request)
|
|
14146
15353
|
},
|
|
14147
15354
|
messages: {
|
|
14148
|
-
read: (request) =>
|
|
14149
|
-
|
|
14150
|
-
if (outcome.ok) session.markDirty();
|
|
14151
|
-
return outcome;
|
|
14152
|
-
}),
|
|
14153
|
-
send: (request) => sendWithState(request),
|
|
15355
|
+
read: (request) => readWithState(request, frontier),
|
|
15356
|
+
send: (request) => sendWithState(request, frontier),
|
|
14154
15357
|
reply: (message, request) => sendWithState({
|
|
14155
15358
|
...request,
|
|
14156
15359
|
target: message.target
|
|
14157
|
-
}),
|
|
15360
|
+
}, frontier),
|
|
14158
15361
|
search: (request) => searchMessages(api, request),
|
|
14159
15362
|
resolve: (request) => resolveMessage(api, request),
|
|
14160
15363
|
react: (request) => reactToMessage(api, request, "add"),
|
|
@@ -14186,6 +15389,7 @@ function createRaft(options) {
|
|
|
14186
15389
|
create: (request) => createTasks(api, request),
|
|
14187
15390
|
unclaim: (request) => unclaimTask(api, request),
|
|
14188
15391
|
assign: (request) => assignTask(api, request),
|
|
15392
|
+
unassign: (request) => unassignTask(api, request),
|
|
14189
15393
|
updateStatus: (request) => updateTaskStatus(api, request),
|
|
14190
15394
|
amend: (request) => amendTask(api, request),
|
|
14191
15395
|
history: (request) => taskHistory(api, request),
|
|
@@ -14220,33 +15424,173 @@ function createRaft(options) {
|
|
|
14220
15424
|
},
|
|
14221
15425
|
routes
|
|
14222
15426
|
};
|
|
15427
|
+
const handlers = {
|
|
15428
|
+
"identity.whoami": async () => whoamiOutcome(await raft.identity.whoami()),
|
|
15429
|
+
"inbox.check": (args, call) => checkWithState(args, call.seen),
|
|
15430
|
+
"inbox.drain": async (args, call) => {
|
|
15431
|
+
const drain = drainWithState(args, call.seen);
|
|
15432
|
+
const batches = [];
|
|
15433
|
+
for (let step = await drain.next();; step = await drain.next()) {
|
|
15434
|
+
if (step.done) return drainedInboxOutcome(batches, step.value);
|
|
15435
|
+
batches.push(step.value);
|
|
15436
|
+
}
|
|
15437
|
+
},
|
|
15438
|
+
"inbox.commit": async (args) => {
|
|
15439
|
+
const result = await commitInbox(args.cursor === void 0 ? void 0 : { cursor: args.cursor });
|
|
15440
|
+
return {
|
|
15441
|
+
ok: true,
|
|
15442
|
+
state: result.cursor === null ? "nothing" : "committed",
|
|
15443
|
+
data: result,
|
|
15444
|
+
next: null,
|
|
15445
|
+
text: result.cursor === null ? "Nothing to commit." : `Committed inbox cursor ${result.cursor}; the next inbox check acknowledges it.${result.saved ? "" : " Saving the state failed; the batch may be delivered again."}`
|
|
15446
|
+
};
|
|
15447
|
+
},
|
|
15448
|
+
"inbox.list": (args) => raft.inbox.list(args),
|
|
15449
|
+
"messages.read": (args, call) => call.origin === "code" ? readWithState({
|
|
15450
|
+
...args,
|
|
15451
|
+
consume: false
|
|
15452
|
+
}, void 0) : readWithState(args, call.seen),
|
|
15453
|
+
"messages.send": (args, call) => sendWithState(args, call.seen),
|
|
15454
|
+
"messages.reply": ({ message, ...request }, call) => sendWithState({
|
|
15455
|
+
...request,
|
|
15456
|
+
target: message.target
|
|
15457
|
+
}, call.seen),
|
|
15458
|
+
"messages.search": (args) => raft.messages.search(args),
|
|
15459
|
+
"messages.resolve": (args) => raft.messages.resolve(args),
|
|
15460
|
+
"messages.react": (args) => raft.messages.react(args),
|
|
15461
|
+
"messages.unreact": (args) => raft.messages.unreact(args),
|
|
15462
|
+
"attachments.comments": (args) => raft.attachments.comments(args),
|
|
15463
|
+
"mentions.pending": (args) => raft.mentions.pending(args),
|
|
15464
|
+
"mentions.execute": (args) => raft.mentions.execute(args),
|
|
15465
|
+
"mentions.deliveries": (args) => raft.mentions.deliveries(args),
|
|
15466
|
+
"actions.prepare": (args) => raft.actions.prepare(args),
|
|
15467
|
+
"manual.get": (args) => raft.manual.get(args),
|
|
15468
|
+
"manual.search": (args) => raft.manual.search(args),
|
|
15469
|
+
"tasks.claim": (args) => raft.tasks.claim(args),
|
|
15470
|
+
"tasks.list": (args) => raft.tasks.list(args),
|
|
15471
|
+
"tasks.create": (args) => raft.tasks.create(args),
|
|
15472
|
+
"tasks.unclaim": (args) => raft.tasks.unclaim(args),
|
|
15473
|
+
"tasks.assign": (args) => raft.tasks.assign(args),
|
|
15474
|
+
"tasks.unassign": (args) => raft.tasks.unassign(args),
|
|
15475
|
+
"tasks.updateStatus": (args) => raft.tasks.updateStatus(args),
|
|
15476
|
+
"tasks.amend": (args) => raft.tasks.amend(args),
|
|
15477
|
+
"tasks.history": (args) => raft.tasks.history(args),
|
|
15478
|
+
"tasks.convert": (args) => raft.tasks.convert(args),
|
|
15479
|
+
"tasks.delete": (args) => raft.tasks.delete(args),
|
|
15480
|
+
"channels.join": (args) => raft.channels.join(args),
|
|
15481
|
+
"channels.leave": (args) => raft.channels.leave(args),
|
|
15482
|
+
"channels.mute": (args) => raft.channels.mute(args),
|
|
15483
|
+
"channels.unmute": (args) => raft.channels.unmute(args),
|
|
15484
|
+
"channels.members": (args) => raft.channels.members(args),
|
|
15485
|
+
"threads.list": () => raft.threads.list(),
|
|
15486
|
+
"threads.unfollow": (args) => raft.threads.unfollow(args),
|
|
15487
|
+
"server.info": (args) => raft.server.info(args),
|
|
15488
|
+
"profile.show": (args) => raft.profile.show(args),
|
|
15489
|
+
"profile.update": (args) => raft.profile.update(args)
|
|
15490
|
+
};
|
|
15491
|
+
const invoke = async (name, args, caller = {}) => {
|
|
15492
|
+
const found = typeof name === "string" ? lookupRaftOperation(name) : void 0;
|
|
15493
|
+
if (!found || !Object.hasOwn(handlers, name)) return failureOutcome(opError("INVALID_REQUEST", {
|
|
15494
|
+
message: `Unknown operation${typeof name === "string" && /^[A-Za-z0-9_.]{1,64}$/.test(name) ? ` "${name}"` : ""}; nothing was sent.`,
|
|
15495
|
+
nextAction: "Use a name from RAFT_OPERATIONS (for example \"messages.send\")."
|
|
15496
|
+
}));
|
|
15497
|
+
const origin = caller?.origin ?? "model";
|
|
15498
|
+
if (origin !== "model" && origin !== "code" || caller?.contextId !== void 0 && typeof caller.contextId !== "string") return failureOutcome(opError("INVALID_REQUEST", { message: "caller.origin must be \"model\" or \"code\", and caller.contextId a string; nothing was sent." }));
|
|
15499
|
+
const input = args === void 0 ? {} : args;
|
|
15500
|
+
const invalid = validateOpRequest(found.schema, input);
|
|
15501
|
+
if (invalid) return invalid;
|
|
15502
|
+
if (origin === "code" && found.spec.modelOnly) return failureOutcome(opError("MODEL_ONLY", { message: `${found.spec.name} only counts when the model sees its result, so it cannot be run from code; nothing was sent.` }));
|
|
15503
|
+
const handler = handlers[name];
|
|
15504
|
+
return handler(input, {
|
|
15505
|
+
seen: frontier.inContext(caller?.contextId),
|
|
15506
|
+
origin
|
|
15507
|
+
});
|
|
15508
|
+
};
|
|
15509
|
+
return {
|
|
15510
|
+
...raft,
|
|
15511
|
+
invoke
|
|
15512
|
+
};
|
|
15513
|
+
}
|
|
15514
|
+
/** `identity.whoami` as an outcome (for `invoke`). */
|
|
15515
|
+
function whoamiOutcome(result) {
|
|
15516
|
+
if (!result.ok) return failureOutcome(opError(result.error.code, {
|
|
15517
|
+
message: result.error.message,
|
|
15518
|
+
...result.status === void 0 ? {} : { status: result.status }
|
|
15519
|
+
}));
|
|
15520
|
+
const { agent, server, capabilities, guide } = result.data;
|
|
15521
|
+
const head = `You are @${agent.name}${agent.displayName ? ` (${agent.displayName})` : ""} on ${server.name}. Credential capabilities: ${capabilities.join(", ") || "none"}.`;
|
|
15522
|
+
return {
|
|
15523
|
+
ok: true,
|
|
15524
|
+
state: "identity",
|
|
15525
|
+
data: result.data,
|
|
15526
|
+
next: null,
|
|
15527
|
+
text: guide ? `${head}\n\n${guide}` : head
|
|
15528
|
+
};
|
|
14223
15529
|
}
|
|
14224
15530
|
//#endregion
|
|
14225
15531
|
exports.RAFT_INBOX_NOTICE_SCHEMA = RAFT_INBOX_NOTICE_SCHEMA;
|
|
15532
|
+
exports.RAFT_JSON_SCHEMA_KEYWORDS = RAFT_JSON_SCHEMA_KEYWORDS;
|
|
14226
15533
|
exports.RAFT_NOTICE_DELIVERY_ID_HEADER = RAFT_NOTICE_DELIVERY_ID_HEADER;
|
|
14227
15534
|
exports.RAFT_NOTICE_SIGNATURE_HEADER = RAFT_NOTICE_SIGNATURE_HEADER;
|
|
15535
|
+
exports.RAFT_OPERATIONS = RAFT_OPERATIONS;
|
|
15536
|
+
exports.RAFT_OPERATIONS_SCHEMA = RAFT_OPERATIONS_SCHEMA;
|
|
15537
|
+
exports.RAFT_OPERATIONS_VERSION = RAFT_OPERATIONS_VERSION;
|
|
14228
15538
|
exports.RAFT_STATE_CONTINUATIONS_PER_TARGET = RAFT_STATE_CONTINUATIONS_PER_TARGET;
|
|
14229
15539
|
exports.RAFT_STATE_CONTINUATIONS_TOTAL = RAFT_STATE_CONTINUATIONS_TOTAL;
|
|
14230
15540
|
exports.RAFT_STATE_SCHEMA = RAFT_STATE_SCHEMA;
|
|
14231
15541
|
exports.RaftCredentialError = RaftCredentialError;
|
|
14232
15542
|
exports.RaftSdkConfigurationError = RaftSdkConfigurationError;
|
|
14233
15543
|
exports.SeenFrontier = SeenFrontier;
|
|
15544
|
+
exports.amendTaskRequestSchema = amendTaskRequestSchema;
|
|
15545
|
+
exports.assignTaskRequestSchema = assignTaskRequestSchema;
|
|
15546
|
+
exports.attachmentCommentsRequestSchema = attachmentCommentsRequestSchema;
|
|
14234
15547
|
exports.bootstrapRaftCredential = bootstrapRaftCredential;
|
|
15548
|
+
exports.channelMembersRequestSchema = channelMembersRequestSchema;
|
|
15549
|
+
exports.channelTargetRequestSchema = channelTargetRequestSchema;
|
|
15550
|
+
exports.checkInboxRequestSchema = checkInboxRequestSchema;
|
|
15551
|
+
exports.claimTasksRequestSchema = claimTasksRequestSchema;
|
|
15552
|
+
exports.commitInboxRequestSchema = commitInboxRequestSchema;
|
|
15553
|
+
exports.convertMessageToTaskRequestSchema = convertMessageToTaskRequestSchema;
|
|
14235
15554
|
exports.createFileCredentialStore = createFileCredentialStore;
|
|
14236
15555
|
exports.createRaft = createRaft;
|
|
14237
15556
|
exports.createRaftClient = createRaftClient;
|
|
14238
15557
|
exports.createRaftClientFromStore = createRaftClientFromStore;
|
|
14239
15558
|
exports.createRaftRoutes = createRaftRoutes;
|
|
15559
|
+
exports.createTasksRequestSchema = createTasksRequestSchema;
|
|
14240
15560
|
exports.describeRaftRoute = describeRaftRoute;
|
|
15561
|
+
exports.drainInboxRequestSchema = drainInboxRequestSchema;
|
|
15562
|
+
exports.executeMentionActionRequestSchema = executeMentionActionRequestSchema;
|
|
15563
|
+
exports.getManualTopicRequestSchema = getManualTopicRequestSchema;
|
|
14241
15564
|
exports.hasAgentMessageIdentity = hasAgentMessageIdentity;
|
|
14242
15565
|
exports.hashRaftSendContent = hashRaftSendContent;
|
|
14243
15566
|
exports.inferAttachmentMimeType = inferAttachmentMimeType;
|
|
14244
15567
|
exports.isInterrupted = isInterrupted;
|
|
14245
15568
|
exports.joinRaftChannelByTarget = joinRaftChannelByTarget;
|
|
15569
|
+
exports.listInboxRequestSchema = listInboxRequestSchema;
|
|
14246
15570
|
exports.listRaftRoutes = listRaftRoutes;
|
|
15571
|
+
exports.listTasksRequestSchema = listTasksRequestSchema;
|
|
15572
|
+
exports.listThreadsRequestSchema = listThreadsRequestSchema;
|
|
14247
15573
|
exports.parseRaftRegularChannelTarget = parseRaftRegularChannelTarget;
|
|
14248
15574
|
exports.parseRaftState = parseRaftState;
|
|
15575
|
+
exports.pendingMentionActionsRequestSchema = pendingMentionActionsRequestSchema;
|
|
15576
|
+
exports.prepareActionCardRequestSchema = prepareActionCardRequestSchema;
|
|
14249
15577
|
exports.projectRaftMessage = projectRaftMessage;
|
|
14250
15578
|
exports.projectRaftMessages = projectRaftMessages;
|
|
15579
|
+
exports.raftToolNameFor = raftToolNameFor;
|
|
15580
|
+
exports.reactRequestSchema = reactRequestSchema;
|
|
15581
|
+
exports.readHistoryRequestSchema = readHistoryRequestSchema;
|
|
14251
15582
|
exports.readLatestReadThread = readLatestReadThread;
|
|
15583
|
+
exports.replyToRequestSchema = replyToRequestSchema;
|
|
15584
|
+
exports.resolveMessageRequestSchema = resolveMessageRequestSchema;
|
|
15585
|
+
exports.searchManualRequestSchema = searchManualRequestSchema;
|
|
15586
|
+
exports.searchMessagesRequestSchema = searchMessagesRequestSchema;
|
|
15587
|
+
exports.sendMessageRequestSchema = sendMessageRequestSchema;
|
|
15588
|
+
exports.senderMentionDeliveriesRequestSchema = senderMentionDeliveriesRequestSchema;
|
|
15589
|
+
exports.serverInfoRequestSchema = serverInfoRequestSchema;
|
|
15590
|
+
exports.showProfileRequestSchema = showProfileRequestSchema;
|
|
15591
|
+
exports.taskRefSchema = taskRefSchema;
|
|
15592
|
+
exports.unfollowThreadRequestSchema = unfollowThreadRequestSchema;
|
|
15593
|
+
exports.updateProfileRequestSchema = updateProfileRequestSchema;
|
|
15594
|
+
exports.updateTaskStatusRequestSchema = updateTaskStatusRequestSchema;
|
|
14252
15595
|
exports.verifyInboxNotice = verifyInboxNotice;
|
|
15596
|
+
exports.whoamiRequestSchema = whoamiRequestSchema;
|