@botharness/core 0.1.0-alpha.1 → 1.0.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/dist/index.d.mts +91 -16
- package/dist/index.mjs +931 -184
- package/package.json +1 -1
package/dist/index.mjs
CHANGED
|
@@ -2828,6 +2828,114 @@ function handleIntersectionResults(result, left, right) {
|
|
|
2828
2828
|
result.value = merged.data;
|
|
2829
2829
|
return result;
|
|
2830
2830
|
}
|
|
2831
|
+
const $ZodTuple = /*@__PURE__*/ $constructor("$ZodTuple", (inst, def) => {
|
|
2832
|
+
$ZodType.init(inst, def);
|
|
2833
|
+
const items = def.items;
|
|
2834
|
+
const memo = globalConfig.memoizer;
|
|
2835
|
+
memo?.attach(inst);
|
|
2836
|
+
inst._zod.parse = (payload, ctx) => {
|
|
2837
|
+
const input = payload.value;
|
|
2838
|
+
if (!Array.isArray(input)) {
|
|
2839
|
+
payload.issues.push({
|
|
2840
|
+
input,
|
|
2841
|
+
inst,
|
|
2842
|
+
expected: "tuple",
|
|
2843
|
+
code: "invalid_type"
|
|
2844
|
+
});
|
|
2845
|
+
return payload;
|
|
2846
|
+
}
|
|
2847
|
+
payload.value = memo ? memo.alloc(inst, payload, [], ctx) : [];
|
|
2848
|
+
const proms = [];
|
|
2849
|
+
const optinStart = getTupleOptStart(items, "optin");
|
|
2850
|
+
const optoutStart = getTupleOptStart(items, "optout");
|
|
2851
|
+
if (!def.rest) {
|
|
2852
|
+
if (input.length < optinStart) {
|
|
2853
|
+
payload.issues.push({
|
|
2854
|
+
code: "too_small",
|
|
2855
|
+
minimum: optinStart,
|
|
2856
|
+
inclusive: true,
|
|
2857
|
+
input,
|
|
2858
|
+
inst,
|
|
2859
|
+
origin: "array"
|
|
2860
|
+
});
|
|
2861
|
+
return payload;
|
|
2862
|
+
}
|
|
2863
|
+
if (input.length > items.length) payload.issues.push({
|
|
2864
|
+
code: "too_big",
|
|
2865
|
+
maximum: items.length,
|
|
2866
|
+
inclusive: true,
|
|
2867
|
+
input,
|
|
2868
|
+
inst,
|
|
2869
|
+
origin: "array"
|
|
2870
|
+
});
|
|
2871
|
+
}
|
|
2872
|
+
const itemResults = new Array(items.length);
|
|
2873
|
+
const abortEarly = def.rest ? ctx?.abortEarly : void 0;
|
|
2874
|
+
let itemAborted = false;
|
|
2875
|
+
for (let i = 0; i < items.length; i++) {
|
|
2876
|
+
const r = items[i]._zod.run({
|
|
2877
|
+
value: input[i],
|
|
2878
|
+
issues: []
|
|
2879
|
+
}, ctx);
|
|
2880
|
+
if (r instanceof Promise) proms.push(r.then((rr) => {
|
|
2881
|
+
itemResults[i] = rr;
|
|
2882
|
+
}));
|
|
2883
|
+
else {
|
|
2884
|
+
itemResults[i] = r;
|
|
2885
|
+
if (abortEarly && !itemAborted && r.issues.length) itemAborted = aborted(r);
|
|
2886
|
+
}
|
|
2887
|
+
}
|
|
2888
|
+
if (def.rest && !itemAborted) {
|
|
2889
|
+
let i = items.length - 1;
|
|
2890
|
+
const rest = input.slice(items.length);
|
|
2891
|
+
let seen = payload.issues.length;
|
|
2892
|
+
for (const el of rest) {
|
|
2893
|
+
if (abortEarly && payload.issues.length !== seen) {
|
|
2894
|
+
if (aborted(payload, seen)) break;
|
|
2895
|
+
seen = payload.issues.length;
|
|
2896
|
+
}
|
|
2897
|
+
i++;
|
|
2898
|
+
const result = def.rest._zod.run({
|
|
2899
|
+
value: el,
|
|
2900
|
+
issues: []
|
|
2901
|
+
}, ctx);
|
|
2902
|
+
if (result instanceof Promise) proms.push(result.then((r) => handleTupleResult(r, payload, i)));
|
|
2903
|
+
else handleTupleResult(result, payload, i);
|
|
2904
|
+
}
|
|
2905
|
+
}
|
|
2906
|
+
if (proms.length) return Promise.all(proms).then(() => handleTupleResults(itemResults, payload, items, input, optoutStart));
|
|
2907
|
+
return handleTupleResults(itemResults, payload, items, input, optoutStart);
|
|
2908
|
+
};
|
|
2909
|
+
});
|
|
2910
|
+
function getTupleOptStart(items, key) {
|
|
2911
|
+
for (let i = items.length - 1; i >= 0; i--) if (!(key === "optin" ? items[i]._zod.optin !== void 0 : items[i]._zod.optout === "optional")) return i + 1;
|
|
2912
|
+
return 0;
|
|
2913
|
+
}
|
|
2914
|
+
function handleTupleResult(result, final, index) {
|
|
2915
|
+
if (result.issues.length) final.issues.push(...prefixIssues(index, result.issues));
|
|
2916
|
+
final.value[index] = result.value;
|
|
2917
|
+
}
|
|
2918
|
+
function handleTupleResults(itemResults, final, items, input, optoutStart) {
|
|
2919
|
+
for (let i = 0; i < items.length; i++) {
|
|
2920
|
+
const r = itemResults[i];
|
|
2921
|
+
const isPresent = i < input.length;
|
|
2922
|
+
if (!isPresent && i >= optoutStart && items[i]._zod.optin === "optional") {
|
|
2923
|
+
final.value.length = i;
|
|
2924
|
+
break;
|
|
2925
|
+
}
|
|
2926
|
+
if (r.issues.length) {
|
|
2927
|
+
if (!isPresent && i >= optoutStart) {
|
|
2928
|
+
final.value.length = i;
|
|
2929
|
+
break;
|
|
2930
|
+
}
|
|
2931
|
+
final.issues.push(...prefixIssues(i, r.issues));
|
|
2932
|
+
}
|
|
2933
|
+
final.value[i] = r.value;
|
|
2934
|
+
}
|
|
2935
|
+
for (let i = final.value.length - 1; i >= input.length; i--) if (items[i]._zod.optout === "optional" && final.value[i] === void 0) final.value.length = i;
|
|
2936
|
+
else break;
|
|
2937
|
+
return final;
|
|
2938
|
+
}
|
|
2831
2939
|
const $ZodEnum = /*@__PURE__*/ $constructor("$ZodEnum", (inst, def) => {
|
|
2832
2940
|
$ZodType.init(inst, def);
|
|
2833
2941
|
const values = getEnumValues(def.entries);
|
|
@@ -4745,6 +4853,58 @@ const intersectionProcessor = (schema, ctx, json, params) => {
|
|
|
4745
4853
|
json.allOf = allOf;
|
|
4746
4854
|
ctx.intersections.push(allOf);
|
|
4747
4855
|
};
|
|
4856
|
+
const tupleProcessor = (schema, ctx, _json, params) => {
|
|
4857
|
+
const json = _json;
|
|
4858
|
+
const def = schema._zod.def;
|
|
4859
|
+
json.type = "array";
|
|
4860
|
+
const prefixPath = ctx.target === "draft-2020-12" ? "prefixItems" : "items";
|
|
4861
|
+
const restPath = ctx.target === "draft-2020-12" ? "items" : ctx.target === "openapi-3.0" ? "items" : "additionalItems";
|
|
4862
|
+
const prefixItems = def.items.map((x, i) => processSchema(x, ctx, {
|
|
4863
|
+
...params,
|
|
4864
|
+
path: [
|
|
4865
|
+
...params.path,
|
|
4866
|
+
prefixPath,
|
|
4867
|
+
i
|
|
4868
|
+
]
|
|
4869
|
+
}));
|
|
4870
|
+
const rest = def.rest ? processSchema(def.rest, ctx, {
|
|
4871
|
+
...params,
|
|
4872
|
+
path: [
|
|
4873
|
+
...params.path,
|
|
4874
|
+
restPath,
|
|
4875
|
+
...ctx.target === "openapi-3.0" ? [def.items.length] : []
|
|
4876
|
+
]
|
|
4877
|
+
}) : null;
|
|
4878
|
+
let minItems = def.items.length;
|
|
4879
|
+
while (minItems > 0) {
|
|
4880
|
+
const item = def.items[minItems - 1];
|
|
4881
|
+
if (!(ctx.io === "input" ? inputOptin(item) !== void 0 : item._zod.optout === "optional")) break;
|
|
4882
|
+
minItems--;
|
|
4883
|
+
}
|
|
4884
|
+
const maxItems = def.items.length;
|
|
4885
|
+
const isClosed = !def.rest;
|
|
4886
|
+
if (ctx.target === "draft-2020-12") {
|
|
4887
|
+
json.prefixItems = prefixItems;
|
|
4888
|
+
if (isClosed) json.items = false;
|
|
4889
|
+
else if (rest) json.items = rest;
|
|
4890
|
+
if (minItems > 0) json.minItems = minItems;
|
|
4891
|
+
if (isClosed) json.maxItems = maxItems;
|
|
4892
|
+
} else if (ctx.target === "openapi-3.0") {
|
|
4893
|
+
json.items = { anyOf: prefixItems };
|
|
4894
|
+
if (rest) json.items.anyOf.push(rest);
|
|
4895
|
+
if (minItems > 0) json.minItems = minItems;
|
|
4896
|
+
if (isClosed) json.maxItems = maxItems;
|
|
4897
|
+
} else {
|
|
4898
|
+
json.items = prefixItems;
|
|
4899
|
+
if (isClosed) json.additionalItems = false;
|
|
4900
|
+
else if (rest) json.additionalItems = rest;
|
|
4901
|
+
if (minItems > 0) json.minItems = minItems;
|
|
4902
|
+
if (isClosed) json.maxItems = maxItems;
|
|
4903
|
+
}
|
|
4904
|
+
const { minimum, maximum } = aggregateChecks(schema);
|
|
4905
|
+
if (typeof minimum === "number") json.minItems = minimum;
|
|
4906
|
+
if (typeof maximum === "number") json.maxItems = maximum;
|
|
4907
|
+
};
|
|
4748
4908
|
const nullableProcessor = (schema, ctx, json, params) => {
|
|
4749
4909
|
const def = schema._zod.def;
|
|
4750
4910
|
const inner = processSchema(def.innerType, ctx, params);
|
|
@@ -5546,6 +5706,39 @@ function intersection(left, right) {
|
|
|
5546
5706
|
right
|
|
5547
5707
|
});
|
|
5548
5708
|
}
|
|
5709
|
+
const ZodTuple = /*@__PURE__*/ $constructor("ZodTuple", (inst, def) => {
|
|
5710
|
+
_ensureDefaultMemoizer();
|
|
5711
|
+
$ZodTuple.init(inst, def);
|
|
5712
|
+
ZodType.init(inst, def);
|
|
5713
|
+
inst._zod.processJSONSchema = (ctx, json, params) => tupleProcessor(inst, ctx, json, params);
|
|
5714
|
+
}, {
|
|
5715
|
+
rest(rest) {
|
|
5716
|
+
return this.clone({
|
|
5717
|
+
...this._zod.def,
|
|
5718
|
+
rest
|
|
5719
|
+
});
|
|
5720
|
+
},
|
|
5721
|
+
partial() {
|
|
5722
|
+
const def = this._zod.def;
|
|
5723
|
+
if (def.checks?.length) throw new Error(".partial() cannot be used on tuple schemas containing refinements");
|
|
5724
|
+
return this.clone({
|
|
5725
|
+
...def,
|
|
5726
|
+
items: def.items.map((item) => new ZodOptional({
|
|
5727
|
+
type: "optional",
|
|
5728
|
+
innerType: item
|
|
5729
|
+
}))
|
|
5730
|
+
});
|
|
5731
|
+
}
|
|
5732
|
+
});
|
|
5733
|
+
function tuple(items, _paramsOrRest, _params) {
|
|
5734
|
+
const hasRest = _paramsOrRest instanceof $ZodType;
|
|
5735
|
+
return new ZodTuple({
|
|
5736
|
+
type: "tuple",
|
|
5737
|
+
items,
|
|
5738
|
+
rest: hasRest ? _paramsOrRest : null,
|
|
5739
|
+
...normalizeParams(hasRest ? _params : _paramsOrRest)
|
|
5740
|
+
});
|
|
5741
|
+
}
|
|
5549
5742
|
const ZodEnum = /*@__PURE__*/ $constructor("ZodEnum", (inst, def) => {
|
|
5550
5743
|
$ZodEnum.init(inst, def);
|
|
5551
5744
|
ZodType.init(inst, def);
|
|
@@ -6910,7 +7103,7 @@ function createInboundMessaging(options) {
|
|
|
6910
7103
|
grantId: value.id,
|
|
6911
7104
|
grantRevision: value.revision,
|
|
6912
7105
|
defaultRevision: messagingDefaults(db, value.platform).revision,
|
|
6913
|
-
receptionRevision: groupReceptionPolicy(db, value.id).revision,
|
|
7106
|
+
...event.conversation.kind === "group" ? { receptionRevision: groupReceptionPolicy(db, value.id).revision } : {},
|
|
6914
7107
|
...value.channelBridge ? { bridgeRevision: value.channelBridge.revision } : {},
|
|
6915
7108
|
...!value.bridgeRoutes && value.receiveTargetChannelId ? { localChannelId: value.receiveTargetChannelId } : {}
|
|
6916
7109
|
};
|
|
@@ -6927,13 +7120,25 @@ function createInboundMessaging(options) {
|
|
|
6927
7120
|
if (!row) throw new MessagingError("grant-unavailable");
|
|
6928
7121
|
return JSON.parse(row.body);
|
|
6929
7122
|
};
|
|
7123
|
+
const receptionChanged = () => {
|
|
7124
|
+
if (closed) return;
|
|
7125
|
+
try {
|
|
7126
|
+
options.onReceptionChanged?.();
|
|
7127
|
+
} catch {
|
|
7128
|
+
options.warn?.("messaging-reception-publication-failed");
|
|
7129
|
+
}
|
|
7130
|
+
};
|
|
6930
7131
|
const stop = (id) => {
|
|
6931
7132
|
clearTimeout(retries.get(id)?.timer);
|
|
6932
7133
|
retries.delete(id);
|
|
6933
7134
|
const lease = leases.get(id);
|
|
6934
7135
|
leases.delete(id);
|
|
6935
7136
|
lease?.controller.abort();
|
|
6936
|
-
|
|
7137
|
+
try {
|
|
7138
|
+
lease?.dispose?.();
|
|
7139
|
+
} finally {
|
|
7140
|
+
if (lease) receptionChanged();
|
|
7141
|
+
}
|
|
6937
7142
|
};
|
|
6938
7143
|
const targetAvailable = (value) => {
|
|
6939
7144
|
if (value.bridgeRoutes) return true;
|
|
@@ -6980,11 +7185,12 @@ function createInboundMessaging(options) {
|
|
|
6980
7185
|
dispose: void 0
|
|
6981
7186
|
};
|
|
6982
7187
|
leases.set(value.id, lease);
|
|
7188
|
+
receptionChanged();
|
|
6983
7189
|
const startedAt = Date.now();
|
|
6984
7190
|
try {
|
|
6985
7191
|
const inspected = await entry.provider.inspect(value.accountRef, value.targetRef);
|
|
6986
|
-
if (inspected.account.fingerprint !== value.fingerprint || inspected.target.digest !== value.targetDigest || (value.receiveScope ? inspected.target.receiveScope?.conversationId !== value.receiveScope.conversationId : !inspected.target.receiveScope)) throw new MessagingError("rebind-required");
|
|
6987
|
-
transaction((db) => initializeGroupReceptionPolicy(db, value.id));
|
|
7192
|
+
if (inspected.account.fingerprint !== value.fingerprint || inspected.target.digest !== value.targetDigest || (value.receiveScope ? inspected.target.receiveScope?.kind !== value.receiveScope.kind || inspected.target.receiveScope?.conversationId !== value.receiveScope.conversationId : !inspected.target.receiveScope)) throw new MessagingError("rebind-required");
|
|
7193
|
+
if (value.receiveScope?.kind === "group") transaction((db) => initializeGroupReceptionPolicy(db, value.id));
|
|
6988
7194
|
const dispose = await entry.consume({
|
|
6989
7195
|
accountRef: value.accountRef,
|
|
6990
7196
|
fingerprint: value.fingerprint,
|
|
@@ -7006,7 +7212,25 @@ function createInboundMessaging(options) {
|
|
|
7006
7212
|
if (closed || leases.get(value.id) !== lease || providers.get(value.providerId)?.token !== entry.token || latest.revision !== value.revision || latest.revokedAt || latest.suspendedReason || options.bindingAvailable?.(latest.bindingId) === false || !options.isBotActive(value.botSlug)) throw new MessagingError("consumer-unavailable");
|
|
7007
7213
|
if (event.fingerprint !== value.fingerprint || event.botId !== value.accountRef) throw new MessagingError("untrusted-source");
|
|
7008
7214
|
if (!latest.receiveScope) return { accepted: true };
|
|
7009
|
-
if (event.conversation.kind !==
|
|
7215
|
+
if (event.conversation.kind !== latest.receiveScope.kind || event.conversation.id !== value.receiveScope.conversationId) return { accepted: true };
|
|
7216
|
+
if (event.conversation.kind === "dm") {
|
|
7217
|
+
if (latest.receiveTargetChannelId || latest.channelBridge || latest.bridgeRoutes) throw new MessagingError("capability-unavailable");
|
|
7218
|
+
const id = transaction((db) => {
|
|
7219
|
+
signal.throwIfAborted();
|
|
7220
|
+
lease.controller.signal.throwIfAborted();
|
|
7221
|
+
if (latest.receiveAfter && Date.parse(event.at) < Date.parse(latest.receiveAfter)) return void 0;
|
|
7222
|
+
const sourceEventId = persistSource(db, latest, event);
|
|
7223
|
+
const policy = options.sourcePolicy.resolveIn(db, latest.botSlug, "human-dm");
|
|
7224
|
+
db.prepare(`INSERT OR IGNORE INTO inbox_admissions
|
|
7225
|
+
(source_event_id, bot_slug, reason, source_policy_revision, source_policy_wake_mode,
|
|
7226
|
+
wake_policy_revision, wake_mode) VALUES (?, ?, ?, ?, ?, ?, ?)`).run(sourceEventId, latest.botSlug, "human-dm", policy.revision, policy.wake, policy.revision, policy.wake === "immediate" ? "all" : policy.wake);
|
|
7227
|
+
return sourceEventId;
|
|
7228
|
+
}, ["source-event", "bot-inbox"]);
|
|
7229
|
+
if (id !== void 0) setImmediate(() => {
|
|
7230
|
+
if (!closed && valid(grant(value.id))) options.onAdmitted(value.botSlug, id);
|
|
7231
|
+
});
|
|
7232
|
+
return { accepted: true };
|
|
7233
|
+
}
|
|
7010
7234
|
if (!event.mentionedAccount) {
|
|
7011
7235
|
lease.ordinaryVerified = true;
|
|
7012
7236
|
if (ordinaryThreadReply(event)) lease.ordinaryThreads.set(event.reply.threadId, event.reply.rootId);
|
|
@@ -7183,6 +7407,7 @@ function createInboundMessaging(options) {
|
|
|
7183
7407
|
return;
|
|
7184
7408
|
}
|
|
7185
7409
|
lease.dispose = dispose;
|
|
7410
|
+
receptionChanged();
|
|
7186
7411
|
notifyPending(grant(value.id));
|
|
7187
7412
|
options.warn?.(JSON.stringify({
|
|
7188
7413
|
event: "messaging-inbound",
|
|
@@ -7192,7 +7417,10 @@ function createInboundMessaging(options) {
|
|
|
7192
7417
|
}));
|
|
7193
7418
|
} catch (error) {
|
|
7194
7419
|
lease.controller.abort();
|
|
7195
|
-
if (leases.get(value.id) === lease)
|
|
7420
|
+
if (leases.get(value.id) === lease) {
|
|
7421
|
+
leases.delete(value.id);
|
|
7422
|
+
receptionChanged();
|
|
7423
|
+
}
|
|
7196
7424
|
const reason = error instanceof MessagingError ? error.code : "consumer-unavailable";
|
|
7197
7425
|
const delay = [
|
|
7198
7426
|
250,
|
|
@@ -7315,6 +7543,7 @@ function createInboundMessaging(options) {
|
|
|
7315
7543
|
async channelBridge(channelId, rawInput) {
|
|
7316
7544
|
const input = channelBridgeInput.parse(rawInput);
|
|
7317
7545
|
const value = grant(input.grantId);
|
|
7546
|
+
if (value.platform === "weixin") throw new MessagingError("capability-unavailable");
|
|
7318
7547
|
const channel = database.read((db) => bridgeChannel(db, channelId, value.botSlug, true));
|
|
7319
7548
|
const target = input.delivery === "inbox" ? null : channelId;
|
|
7320
7549
|
if (target === null && channel.type !== "dm") throw new MessagingError("channel-unavailable");
|
|
@@ -7392,6 +7621,7 @@ function createInboundMessaging(options) {
|
|
|
7392
7621
|
if (providers.get(value.providerId) !== entry || inspected.account.fingerprint !== value.fingerprint || inspected.target.digest !== value.targetDigest) throw new MessagingError("rebind-required");
|
|
7393
7622
|
scope = inspected.target.receiveScope;
|
|
7394
7623
|
if (!scope) throw new MessagingError("group-required");
|
|
7624
|
+
if (scope.kind === "dm" && (value.receiveTargetChannelId || value.channelBridge || value.bridgeRoutes)) throw new MessagingError("capability-unavailable");
|
|
7395
7625
|
}
|
|
7396
7626
|
const updated = database.transaction((db) => {
|
|
7397
7627
|
const latest = grant(id);
|
|
@@ -8034,6 +8264,7 @@ function createOutboundMessaging(options) {
|
|
|
8034
8264
|
sourcePolicy: options.sourcePolicy ?? createBotSourcePolicyStore(database),
|
|
8035
8265
|
isBotActive: options.isBotActive,
|
|
8036
8266
|
onAdmitted: options.onAdmitted ?? (() => void 0),
|
|
8267
|
+
...options.onReceptionChanged ? { onReceptionChanged: options.onReceptionChanged } : {},
|
|
8037
8268
|
...options.onPlaced ? { onPlaced: options.onPlaced } : {},
|
|
8038
8269
|
...options.onShared ? { onShared: options.onShared } : {},
|
|
8039
8270
|
...options.warn === void 0 ? {} : { warn: options.warn }
|
|
@@ -8326,7 +8557,7 @@ function createOutboundMessaging(options) {
|
|
|
8326
8557
|
const channel = database.read((db) => humanBridgeChannel(db, channelId));
|
|
8327
8558
|
const snapshots = await Promise.all(channel.members.map((slug) => service.snapshot(slug)));
|
|
8328
8559
|
const current = database.read((db) => humanBridgeChannel(db, channelId));
|
|
8329
|
-
const grants = snapshots.flatMap((snapshot, index) => current.members.includes(channel.members[index]) ? snapshot.grants : []);
|
|
8560
|
+
const grants = snapshots.flatMap((snapshot, index) => current.members.includes(channel.members[index]) ? snapshot.grants.filter((grant) => grant.platform !== "weixin") : []);
|
|
8330
8561
|
const source = (g) => ({
|
|
8331
8562
|
grantId: g.id,
|
|
8332
8563
|
grantRevision: g.revision,
|
|
@@ -8399,7 +8630,7 @@ function createOutboundMessaging(options) {
|
|
|
8399
8630
|
reception: inbound.status(value.id),
|
|
8400
8631
|
canReceive,
|
|
8401
8632
|
canPost,
|
|
8402
|
-
...current.revokedAt === void 0 && !current.suspendedReason && options.isBotActive(botSlug) ? {
|
|
8633
|
+
...current.revokedAt === void 0 && !current.suspendedReason && options.isBotActive(botSlug) && current.receiveScope?.kind !== "dm" && current.platform !== "weixin" ? {
|
|
8403
8634
|
groupPolicy: inbound.policy(botSlug, value.id),
|
|
8404
8635
|
threadPolicies: inbound.threads(botSlug, value.id),
|
|
8405
8636
|
ordinaryDelivery: inbound.ordinaryDelivery(value.id)
|
|
@@ -8758,7 +8989,8 @@ const inboundSchema = object({
|
|
|
8758
8989
|
channel: _enum([
|
|
8759
8990
|
"feishu",
|
|
8760
8991
|
"slack",
|
|
8761
|
-
"discord"
|
|
8992
|
+
"discord",
|
|
8993
|
+
"weixin"
|
|
8762
8994
|
]),
|
|
8763
8995
|
botId: identifier,
|
|
8764
8996
|
fingerprint: string().regex(/^[a-f0-9]{64}$/),
|
|
@@ -8822,6 +9054,8 @@ function providerFailure(error) {
|
|
|
8822
9054
|
"bot-not-connected",
|
|
8823
9055
|
"bad-request",
|
|
8824
9056
|
"stale-route",
|
|
9057
|
+
"source-not-found",
|
|
9058
|
+
"reply-permission-denied",
|
|
8825
9059
|
"consumer-unavailable",
|
|
8826
9060
|
"file-upload-failed",
|
|
8827
9061
|
"file-provider-rejected"
|
|
@@ -8856,6 +9090,9 @@ function createDshImProvider(value, platform = "feishu") {
|
|
|
8856
9090
|
...platform === "feishu" && target.kind === "group" && typeof target.route.chatId === "string" || platform === "slack" && target.kind === "conversation" && typeof target.route.channelId === "string" || platform === "discord" && target.kind === "channel" && typeof target.route.channelId === "string" ? { receiveScope: {
|
|
8857
9091
|
kind: "group",
|
|
8858
9092
|
conversationId: String(platform === "feishu" ? target.route.chatId : target.route.channelId)
|
|
9093
|
+
} } : platform === "weixin" && target.kind === "user" && typeof target.route.toUserId === "string" ? { receiveScope: {
|
|
9094
|
+
kind: "dm",
|
|
9095
|
+
conversationId: target.route.toUserId
|
|
8859
9096
|
} } : {}
|
|
8860
9097
|
}));
|
|
8861
9098
|
return {
|
|
@@ -8983,12 +9220,18 @@ function createDshImProvider(value, platform = "feishu") {
|
|
|
8983
9220
|
const receipt = object({
|
|
8984
9221
|
version: literal(1),
|
|
8985
9222
|
messageId: identifier,
|
|
8986
|
-
conversationId: identifier
|
|
9223
|
+
conversationId: identifier,
|
|
9224
|
+
identityKind: literal("client-acknowledgement").optional()
|
|
8987
9225
|
}).strict().safeParse(result.receipt);
|
|
8988
|
-
if (!receipt.success || receipt.data.conversationId !== input.route.conversationId) throw new MessagingProviderError("provider-result-unknown", "unknown");
|
|
9226
|
+
if (!receipt.success || receipt.data.conversationId !== input.route.conversationId || (platform === "weixin" ? receipt.data.identityKind !== "client-acknowledgement" : receipt.data.identityKind !== void 0)) throw new MessagingProviderError("provider-result-unknown", "unknown");
|
|
8989
9227
|
return {
|
|
8990
9228
|
accepted: true,
|
|
8991
|
-
receipt:
|
|
9229
|
+
receipt: {
|
|
9230
|
+
version: 1,
|
|
9231
|
+
messageId: receipt.data.messageId,
|
|
9232
|
+
conversationId: receipt.data.conversationId,
|
|
9233
|
+
...receipt.data.identityKind ? { identityKind: receipt.data.identityKind } : {}
|
|
9234
|
+
}
|
|
8992
9235
|
};
|
|
8993
9236
|
}
|
|
8994
9237
|
return { accepted: true };
|
|
@@ -17383,20 +17626,78 @@ function isModelPreset(value) {
|
|
|
17383
17626
|
const preset = value;
|
|
17384
17627
|
return typeof preset["id"] === "string" && typeof preset["name"] === "string" && Number.isSafeInteger(preset["revision"]) && typeof preset["createdAt"] === "string" && isModelRoute(preset["orchestrator"]) && isModelRoute(preset["assignmentDefault"]) && (preset["assignmentModels"] === void 0 || isAssignmentModelsForDefault(preset["assignmentDefault"], preset["assignmentModels"]));
|
|
17385
17628
|
}
|
|
17386
|
-
function
|
|
17387
|
-
const
|
|
17388
|
-
|
|
17389
|
-
|
|
17629
|
+
function presetRecord(preset) {
|
|
17630
|
+
const route = (value) => ({
|
|
17631
|
+
provider: value.provider,
|
|
17632
|
+
model: value.model,
|
|
17633
|
+
...value.reasoningEffort === void 0 ? {} : { reasoningEffort: value.reasoningEffort }
|
|
17634
|
+
});
|
|
17635
|
+
return {
|
|
17636
|
+
id: preset.id,
|
|
17637
|
+
name: preset.name,
|
|
17638
|
+
revision: preset.revision,
|
|
17639
|
+
createdAt: preset.createdAt,
|
|
17640
|
+
orchestrator: route(preset.orchestrator),
|
|
17641
|
+
assignmentDefault: route(preset.assignmentDefault),
|
|
17642
|
+
...preset.assignmentModels === void 0 ? {} : { assignmentModels: preset.assignmentModels.map((option) => ({
|
|
17643
|
+
provider: option.provider,
|
|
17644
|
+
model: option.model,
|
|
17645
|
+
allowedEfforts: [...option.allowedEfforts],
|
|
17646
|
+
defaultEffort: option.defaultEffort
|
|
17647
|
+
})) }
|
|
17648
|
+
};
|
|
17649
|
+
}
|
|
17650
|
+
function createModelPresetStore(options) {
|
|
17651
|
+
const now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
17652
|
+
const reportImport = (event) => {
|
|
17390
17653
|
try {
|
|
17391
|
-
|
|
17654
|
+
options.onImport?.(event);
|
|
17655
|
+
} catch {}
|
|
17656
|
+
};
|
|
17657
|
+
const port = attachOperationalModule(options.database, "model-presets");
|
|
17658
|
+
const read = () => port.read((database) => {
|
|
17659
|
+
return database.prepare("SELECT body FROM model_presets ORDER BY rowid").all().map((row) => {
|
|
17660
|
+
const parsed = JSON.parse(String(row["body"]));
|
|
17661
|
+
if (!isModelPreset(parsed)) throw new Error("Invalid Model Preset record");
|
|
17662
|
+
return parsed;
|
|
17663
|
+
});
|
|
17664
|
+
});
|
|
17665
|
+
const write = (preset) => {
|
|
17666
|
+
port.transaction((database) => {
|
|
17667
|
+
database.prepare(`INSERT INTO model_presets (id, body) VALUES (?, ?)
|
|
17668
|
+
ON CONFLICT (id) DO UPDATE SET body = excluded.body`).run(preset.id, JSON.stringify(presetRecord(preset)));
|
|
17669
|
+
}, ["model-presets"]);
|
|
17670
|
+
};
|
|
17671
|
+
if (options.database.mode === "ready" && !port.read((database) => database.prepare("SELECT singleton FROM model_presets_import WHERE singleton = 1").get())) {
|
|
17672
|
+
const startedAt = performance.now();
|
|
17673
|
+
try {
|
|
17674
|
+
let legacy;
|
|
17675
|
+
try {
|
|
17676
|
+
legacy = JSON.parse(readFileSync(join(options.rootDir, "model-presets.json"), "utf8"));
|
|
17677
|
+
} catch (error) {
|
|
17678
|
+
if (error.code !== "ENOENT") throw new Error("Invalid Model Preset import source");
|
|
17679
|
+
legacy = [];
|
|
17680
|
+
}
|
|
17681
|
+
if (!Array.isArray(legacy) || !legacy.every(isModelPreset) || legacy.some((preset) => !preset.id.trim() || !preset.name.trim() || preset.revision < 1) || new Set(legacy.map((preset) => preset.id)).size !== legacy.length || new Set(legacy.map((preset) => preset.name.toLowerCase())).size !== legacy.length) throw new Error("Invalid Model Preset import source");
|
|
17682
|
+
port.transaction((database) => {
|
|
17683
|
+
if (database.prepare("SELECT id FROM model_presets LIMIT 1").get()) throw new Error("Model Preset target is populated without an import marker");
|
|
17684
|
+
const insert = database.prepare("INSERT INTO model_presets (id, body) VALUES (?, ?)");
|
|
17685
|
+
for (const preset of legacy) insert.run(preset.id, JSON.stringify(presetRecord(preset)));
|
|
17686
|
+
database.prepare("INSERT INTO model_presets_import (singleton, imported_at) VALUES (1, ?)").run(now().toISOString());
|
|
17687
|
+
}, ["model-presets"]);
|
|
17688
|
+
reportImport({
|
|
17689
|
+
phase: "complete",
|
|
17690
|
+
count: legacy.length,
|
|
17691
|
+
durationMs: performance.now() - startedAt
|
|
17692
|
+
});
|
|
17392
17693
|
} catch (error) {
|
|
17393
|
-
|
|
17694
|
+
reportImport({
|
|
17695
|
+
phase: "failed",
|
|
17696
|
+
durationMs: performance.now() - startedAt
|
|
17697
|
+
});
|
|
17394
17698
|
throw error;
|
|
17395
17699
|
}
|
|
17396
|
-
|
|
17397
|
-
if (!Array.isArray(parsed) || !parsed.every(isModelPreset)) throw new Error("Invalid Model Preset store");
|
|
17398
|
-
return parsed;
|
|
17399
|
-
};
|
|
17700
|
+
}
|
|
17400
17701
|
return {
|
|
17401
17702
|
list: read,
|
|
17402
17703
|
get: (id) => read().find((preset) => preset.id === id),
|
|
@@ -17404,8 +17705,7 @@ function createModelPresetStore(rootDir, now = () => /* @__PURE__ */ new Date())
|
|
|
17404
17705
|
if (input.assignmentModels !== void 0) validateAssignmentModels(input.assignmentDefault, input.assignmentModels);
|
|
17405
17706
|
const name = input.name.trim();
|
|
17406
17707
|
if (name.length === 0 || name.length > 100) throw new Error("Model Preset name is required");
|
|
17407
|
-
|
|
17408
|
-
if (presets.some((preset) => preset.name.toLowerCase() === name.toLowerCase())) throw new Error("Model Preset name already exists");
|
|
17708
|
+
if (read().some((preset) => preset.name.toLowerCase() === name.toLowerCase())) throw new Error("Model Preset name already exists");
|
|
17409
17709
|
const preset = {
|
|
17410
17710
|
id: randomUUID(),
|
|
17411
17711
|
name,
|
|
@@ -17418,8 +17718,7 @@ function createModelPresetStore(rootDir, now = () => /* @__PURE__ */ new Date())
|
|
|
17418
17718
|
})) },
|
|
17419
17719
|
createdAt: now().toISOString()
|
|
17420
17720
|
};
|
|
17421
|
-
|
|
17422
|
-
atomicWriteFile(file, `${JSON.stringify([...presets, preset], null, 2)}\n`);
|
|
17721
|
+
write(preset);
|
|
17423
17722
|
return preset;
|
|
17424
17723
|
},
|
|
17425
17724
|
update(id, input) {
|
|
@@ -17444,8 +17743,7 @@ function createModelPresetStore(rootDir, now = () => /* @__PURE__ */ new Date())
|
|
|
17444
17743
|
allowedEfforts: [...option.allowedEfforts]
|
|
17445
17744
|
})) }
|
|
17446
17745
|
};
|
|
17447
|
-
|
|
17448
|
-
atomicWriteFile(file, `${JSON.stringify(presets, null, 2)}\n`);
|
|
17746
|
+
write(updated);
|
|
17449
17747
|
return updated;
|
|
17450
17748
|
}
|
|
17451
17749
|
};
|
|
@@ -19324,33 +19622,50 @@ function nextGlobalState(state, patch) {
|
|
|
19324
19622
|
if (topOrder !== void 0) next.topOrder = [...topOrder];
|
|
19325
19623
|
return next;
|
|
19326
19624
|
}
|
|
19625
|
+
const arrangementRecord = object({
|
|
19626
|
+
state: rosterDomainState,
|
|
19627
|
+
sections: array(tuple([string().min(1), rosterSectionRecord]))
|
|
19628
|
+
});
|
|
19629
|
+
function validateArrangement(value) {
|
|
19630
|
+
const parsed = arrangementRecord.parse(value);
|
|
19631
|
+
const sectionIds = /* @__PURE__ */ new Set();
|
|
19632
|
+
const membership = /* @__PURE__ */ new Set();
|
|
19633
|
+
for (const [id, section] of parsed.sections) {
|
|
19634
|
+
if (sectionIds.has(id)) throw new Error("Duplicate roster section");
|
|
19635
|
+
sectionIds.add(id);
|
|
19636
|
+
for (const channelId of section.channelIds) {
|
|
19637
|
+
if (channelId.length === 0 || membership.has(channelId)) throw new Error("Invalid roster membership");
|
|
19638
|
+
membership.add(channelId);
|
|
19639
|
+
}
|
|
19640
|
+
}
|
|
19641
|
+
return parsed;
|
|
19642
|
+
}
|
|
19327
19643
|
var RosterStore = class {
|
|
19328
19644
|
options;
|
|
19329
|
-
|
|
19330
|
-
|
|
19331
|
-
|
|
19645
|
+
database;
|
|
19646
|
+
imported = false;
|
|
19647
|
+
draft;
|
|
19332
19648
|
opening;
|
|
19333
19649
|
closing = false;
|
|
19334
19650
|
warned = false;
|
|
19335
19651
|
tail = Promise.resolve();
|
|
19336
|
-
constructor(options
|
|
19652
|
+
constructor(options) {
|
|
19337
19653
|
this.options = options;
|
|
19654
|
+
this.database = attachOperationalModule(options.database, "roster");
|
|
19655
|
+
if (options.database.mode === "ready") {
|
|
19656
|
+
this.imported = this.database.read((database) => database.prepare("SELECT singleton FROM roster_arrangement_import WHERE singleton = 1").get() !== void 0);
|
|
19657
|
+
if (this.imported) this.readData();
|
|
19658
|
+
}
|
|
19338
19659
|
}
|
|
19339
19660
|
get available() {
|
|
19340
|
-
return this.
|
|
19661
|
+
return this.imported && !this.closing && this.options.database.mode === "ready";
|
|
19341
19662
|
}
|
|
19342
19663
|
async attach(facility) {
|
|
19343
19664
|
this.closing = false;
|
|
19344
|
-
|
|
19345
|
-
|
|
19346
|
-
|
|
19347
|
-
|
|
19348
|
-
}
|
|
19349
|
-
this.domain = domain;
|
|
19350
|
-
this.table = domain.table("sections");
|
|
19351
|
-
this.global = domain.global;
|
|
19352
|
-
this.warned = false;
|
|
19353
|
-
});
|
|
19665
|
+
if (this.imported) return;
|
|
19666
|
+
if (this.opening !== void 0) return this.opening;
|
|
19667
|
+
if (this.options.database.mode !== "ready") throw new RosterUnavailableError();
|
|
19668
|
+
const opening = this.importLegacy(facility);
|
|
19354
19669
|
this.opening = opening;
|
|
19355
19670
|
try {
|
|
19356
19671
|
await opening;
|
|
@@ -19358,15 +19673,36 @@ var RosterStore = class {
|
|
|
19358
19673
|
if (this.opening === opening) this.opening = void 0;
|
|
19359
19674
|
}
|
|
19360
19675
|
}
|
|
19676
|
+
async importLegacy(facility) {
|
|
19677
|
+
const started = performance.now();
|
|
19678
|
+
let domain;
|
|
19679
|
+
try {
|
|
19680
|
+
domain = await facility.open(rosterDomainSpec);
|
|
19681
|
+
if (this.closing) return;
|
|
19682
|
+
const record = validateArrangement({
|
|
19683
|
+
state: domain.global.get(),
|
|
19684
|
+
sections: [...domain.table("sections").entries()]
|
|
19685
|
+
});
|
|
19686
|
+
this.database.transaction((database) => {
|
|
19687
|
+
if (database.prepare("SELECT singleton FROM roster_arrangement WHERE singleton = 1").get() !== void 0) throw new Error("Roster target exists without an import marker");
|
|
19688
|
+
database.prepare("INSERT INTO roster_arrangement(singleton, body) VALUES (1, ?)").run(JSON.stringify(record));
|
|
19689
|
+
database.prepare("INSERT INTO roster_arrangement_import(singleton, imported_at) VALUES (1, ?)").run((/* @__PURE__ */ new Date()).toISOString());
|
|
19690
|
+
});
|
|
19691
|
+
this.imported = true;
|
|
19692
|
+
this.warned = false;
|
|
19693
|
+
this.notifyCommitted();
|
|
19694
|
+
this.warn(`roster-import initiator=host-startup phase=completed count=${record.sections.length} durationMs=${Math.round(performance.now() - started)}`);
|
|
19695
|
+
} catch {
|
|
19696
|
+
this.warn(`roster-import initiator=host-startup phase=failed durationMs=${Math.round(performance.now() - started)}`);
|
|
19697
|
+
throw new RosterUnavailableError();
|
|
19698
|
+
} finally {
|
|
19699
|
+
if (domain !== void 0) await domain.close().catch(() => this.warn("roster-import initiator=host-startup phase=source-close-failed"));
|
|
19700
|
+
}
|
|
19701
|
+
}
|
|
19361
19702
|
async detach() {
|
|
19362
19703
|
this.closing = true;
|
|
19363
|
-
|
|
19364
|
-
|
|
19365
|
-
const domain = this.domain;
|
|
19366
|
-
this.domain = void 0;
|
|
19367
|
-
this.table = void 0;
|
|
19368
|
-
this.global = void 0;
|
|
19369
|
-
if (domain !== void 0) await domain.close();
|
|
19704
|
+
if (this.opening !== void 0) await this.opening.catch(() => {});
|
|
19705
|
+
await this.tail;
|
|
19370
19706
|
}
|
|
19371
19707
|
snapshot() {
|
|
19372
19708
|
const table = this.requireTable();
|
|
@@ -19383,27 +19719,22 @@ var RosterStore = class {
|
|
|
19383
19719
|
};
|
|
19384
19720
|
}
|
|
19385
19721
|
sectionCreate(name) {
|
|
19386
|
-
return this.enqueue(
|
|
19722
|
+
return this.enqueue(() => {
|
|
19387
19723
|
const table = this.requireTable();
|
|
19388
19724
|
const global = this.requireGlobal();
|
|
19389
19725
|
const id = randomUUID();
|
|
19390
|
-
|
|
19726
|
+
table.put(id, {
|
|
19391
19727
|
name,
|
|
19392
19728
|
channelIds: []
|
|
19393
19729
|
});
|
|
19394
19730
|
const state = global.get();
|
|
19395
|
-
|
|
19396
|
-
|
|
19397
|
-
|
|
19398
|
-
|
|
19399
|
-
|
|
19400
|
-
|
|
19401
|
-
|
|
19402
|
-
}));
|
|
19403
|
-
} catch (error) {
|
|
19404
|
-
await table.delete(id).catch(() => {});
|
|
19405
|
-
throw error;
|
|
19406
|
-
}
|
|
19731
|
+
global.set(nextGlobalState(state, {
|
|
19732
|
+
sectionOrder: [id, ...state.sectionOrder],
|
|
19733
|
+
topOrder: state.topOrder === void 0 ? void 0 : [{
|
|
19734
|
+
kind: "section",
|
|
19735
|
+
id
|
|
19736
|
+
}, ...state.topOrder]
|
|
19737
|
+
}));
|
|
19407
19738
|
return {
|
|
19408
19739
|
id,
|
|
19409
19740
|
name,
|
|
@@ -19412,11 +19743,11 @@ var RosterStore = class {
|
|
|
19412
19743
|
});
|
|
19413
19744
|
}
|
|
19414
19745
|
sectionRename(sectionId, name) {
|
|
19415
|
-
return this.enqueue(
|
|
19746
|
+
return this.enqueue(() => {
|
|
19416
19747
|
const table = this.requireTable();
|
|
19417
19748
|
const current = table.get(sectionId);
|
|
19418
19749
|
if (current === void 0) throw new RosterUnknownSectionError(sectionId);
|
|
19419
|
-
if (current.name !== name)
|
|
19750
|
+
if (current.name !== name) table.put(sectionId, {
|
|
19420
19751
|
name,
|
|
19421
19752
|
channelIds: [...current.channelIds]
|
|
19422
19753
|
});
|
|
@@ -19428,14 +19759,14 @@ var RosterStore = class {
|
|
|
19428
19759
|
});
|
|
19429
19760
|
}
|
|
19430
19761
|
sectionRemove(sectionId) {
|
|
19431
|
-
return this.enqueue(
|
|
19762
|
+
return this.enqueue(() => {
|
|
19432
19763
|
const table = this.requireTable();
|
|
19433
19764
|
const global = this.requireGlobal();
|
|
19434
19765
|
const removed = table.get(sectionId);
|
|
19435
|
-
if (!
|
|
19766
|
+
if (!table.delete(sectionId)) return false;
|
|
19436
19767
|
const state = global.get();
|
|
19437
19768
|
const members = removed === void 0 ? [] : [...removed.channelIds];
|
|
19438
|
-
|
|
19769
|
+
global.set(nextGlobalState(state, {
|
|
19439
19770
|
sectionOrder: state.sectionOrder.filter((id) => id !== sectionId),
|
|
19440
19771
|
topOrder: state.topOrder === void 0 ? void 0 : sanitizeTopOrder(state.topOrder.flatMap((entry) => entry.kind === "section" && entry.id === sectionId ? members.map((id) => ({
|
|
19441
19772
|
kind: "channel",
|
|
@@ -19446,7 +19777,7 @@ var RosterStore = class {
|
|
|
19446
19777
|
});
|
|
19447
19778
|
}
|
|
19448
19779
|
channelAssign(channelId, sectionId, index) {
|
|
19449
|
-
return this.enqueue(
|
|
19780
|
+
return this.enqueue(() => {
|
|
19450
19781
|
const table = this.requireTable();
|
|
19451
19782
|
const global = this.requireGlobal();
|
|
19452
19783
|
if (sectionId !== void 0 && table.get(sectionId) === void 0) throw new RosterUnknownSectionError(sectionId);
|
|
@@ -19472,19 +19803,19 @@ var RosterStore = class {
|
|
|
19472
19803
|
channelIds: without
|
|
19473
19804
|
}]);
|
|
19474
19805
|
}
|
|
19475
|
-
for (const [id, record] of removals)
|
|
19476
|
-
if (target !== void 0)
|
|
19806
|
+
for (const [id, record] of removals) table.put(id, record);
|
|
19807
|
+
if (target !== void 0) table.put(target[0], target[1]);
|
|
19477
19808
|
const state = global.get();
|
|
19478
19809
|
if (state.topOrder === void 0) return;
|
|
19479
19810
|
const next = sanitizeTopOrder(sectionId === void 0 ? [...state.topOrder, {
|
|
19480
19811
|
kind: "channel",
|
|
19481
19812
|
id: channelId
|
|
19482
19813
|
}] : state.topOrder.filter((entry) => entry.kind !== "channel" || entry.id !== channelId), table);
|
|
19483
|
-
if (!sameEntries(next, state.topOrder))
|
|
19814
|
+
if (!sameEntries(next, state.topOrder)) global.set(nextGlobalState(state, { topOrder: next }));
|
|
19484
19815
|
});
|
|
19485
19816
|
}
|
|
19486
19817
|
sectionReorder(order) {
|
|
19487
|
-
return this.enqueue(
|
|
19818
|
+
return this.enqueue(() => {
|
|
19488
19819
|
const table = this.requireTable();
|
|
19489
19820
|
const global = this.requireGlobal();
|
|
19490
19821
|
const current = this.orderedSections(global.get().sectionOrder).map((section) => section.id);
|
|
@@ -19515,7 +19846,7 @@ var RosterStore = class {
|
|
|
19515
19846
|
}
|
|
19516
19847
|
const storedTop = state.topOrder;
|
|
19517
19848
|
if (sameIds(next, state.sectionOrder) && (topOrder === void 0 || storedTop !== void 0 && sameEntries(topOrder, storedTop))) return next;
|
|
19518
|
-
|
|
19849
|
+
global.set(nextGlobalState(state, {
|
|
19519
19850
|
sectionOrder: next,
|
|
19520
19851
|
topOrder
|
|
19521
19852
|
}));
|
|
@@ -19523,19 +19854,19 @@ var RosterStore = class {
|
|
|
19523
19854
|
});
|
|
19524
19855
|
}
|
|
19525
19856
|
topReorder(order) {
|
|
19526
|
-
return this.enqueue(
|
|
19857
|
+
return this.enqueue(() => {
|
|
19527
19858
|
const table = this.requireTable();
|
|
19528
19859
|
const global = this.requireGlobal();
|
|
19529
19860
|
const state = global.get();
|
|
19530
19861
|
const next = sanitizeTopOrder(order, table);
|
|
19531
19862
|
const current = state.topOrder === void 0 ? void 0 : sanitizeTopOrder(state.topOrder, table);
|
|
19532
19863
|
if (current !== void 0 && sameEntries(next, current)) return next;
|
|
19533
|
-
|
|
19864
|
+
global.set(nextGlobalState(state, { topOrder: next }));
|
|
19534
19865
|
return next;
|
|
19535
19866
|
});
|
|
19536
19867
|
}
|
|
19537
19868
|
applyBatch(change, resolvePin = (pin) => pin) {
|
|
19538
|
-
return this.enqueue(
|
|
19869
|
+
return this.enqueue(() => {
|
|
19539
19870
|
const ids = uniqueStrings(change.channelIds);
|
|
19540
19871
|
if (ids.length === 0 || change.channelIds.length > 100 || ids.length !== change.channelIds.length) throw new RangeError(`roster batch must contain 1–100 distinct channels`);
|
|
19541
19872
|
const table = this.requireTable();
|
|
@@ -19546,19 +19877,19 @@ var RosterStore = class {
|
|
|
19546
19877
|
if (change.action === "pin") {
|
|
19547
19878
|
const existing = new Set(pins);
|
|
19548
19879
|
const next = [...pins, ...ids.filter((id) => !existing.has(id))];
|
|
19549
|
-
if (!sameIds(next, state.pins))
|
|
19880
|
+
if (!sameIds(next, state.pins)) global.set(nextGlobalState(state, { pins: next }));
|
|
19550
19881
|
return this.snapshot();
|
|
19551
19882
|
}
|
|
19552
19883
|
if (change.action === "hide") {
|
|
19553
19884
|
const hidden = uniqueStrings(state.hidden ?? []);
|
|
19554
19885
|
const existing = new Set(hidden);
|
|
19555
19886
|
const next = [...hidden, ...ids.filter((id) => !existing.has(id))];
|
|
19556
|
-
if (!sameIds(next, state.hidden ?? []))
|
|
19887
|
+
if (!sameIds(next, state.hidden ?? [])) global.set(nextGlobalState(state, { hidden: next }));
|
|
19557
19888
|
return this.snapshot();
|
|
19558
19889
|
}
|
|
19559
19890
|
const nextPins = pins.filter((id) => !selected.has(id));
|
|
19560
19891
|
if (change.action === "unpin") {
|
|
19561
|
-
if (!sameIds(nextPins, state.pins))
|
|
19892
|
+
if (!sameIds(nextPins, state.pins)) global.set(nextGlobalState(state, { pins: nextPins }));
|
|
19562
19893
|
return this.snapshot();
|
|
19563
19894
|
}
|
|
19564
19895
|
const sectionId = change.sectionId;
|
|
@@ -19572,12 +19903,12 @@ var RosterStore = class {
|
|
|
19572
19903
|
channelIds
|
|
19573
19904
|
}]);
|
|
19574
19905
|
}
|
|
19575
|
-
for (const [id, record] of updates)
|
|
19906
|
+
for (const [id, record] of updates) table.put(id, record);
|
|
19576
19907
|
const topOrder = state.topOrder === void 0 ? void 0 : sanitizeTopOrder([...state.topOrder.filter((entry) => entry.kind !== "channel" || !selected.has(entry.id)), ...sectionId === void 0 ? ids.map((id) => ({
|
|
19577
19908
|
kind: "channel",
|
|
19578
19909
|
id
|
|
19579
19910
|
})) : []], table);
|
|
19580
|
-
if (!sameIds(nextPins, state.pins) || topOrder !== void 0 && (state.topOrder === void 0 || !sameEntries(topOrder, state.topOrder)))
|
|
19911
|
+
if (!sameIds(nextPins, state.pins) || topOrder !== void 0 && (state.topOrder === void 0 || !sameEntries(topOrder, state.topOrder))) global.set(nextGlobalState(state, {
|
|
19581
19912
|
pins: nextPins,
|
|
19582
19913
|
topOrder
|
|
19583
19914
|
}));
|
|
@@ -19585,20 +19916,20 @@ var RosterStore = class {
|
|
|
19585
19916
|
});
|
|
19586
19917
|
}
|
|
19587
19918
|
pinsSet(pins) {
|
|
19588
|
-
return this.enqueue(
|
|
19919
|
+
return this.enqueue(() => {
|
|
19589
19920
|
const global = this.requireGlobal();
|
|
19590
19921
|
const next = uniqueStrings(pins);
|
|
19591
19922
|
const state = global.get();
|
|
19592
|
-
if (!sameIds(next, state.pins))
|
|
19923
|
+
if (!sameIds(next, state.pins)) global.set(nextGlobalState(state, { pins: next }));
|
|
19593
19924
|
return next;
|
|
19594
19925
|
});
|
|
19595
19926
|
}
|
|
19596
19927
|
hiddenSet(hidden) {
|
|
19597
|
-
return this.enqueue(
|
|
19928
|
+
return this.enqueue(() => {
|
|
19598
19929
|
const global = this.requireGlobal();
|
|
19599
19930
|
const next = uniqueStrings(hidden);
|
|
19600
19931
|
const state = global.get();
|
|
19601
|
-
if (!sameIds(next, state.hidden ?? []))
|
|
19932
|
+
if (!sameIds(next, state.hidden ?? [])) global.set(nextGlobalState(state, { hidden: next }));
|
|
19602
19933
|
return next;
|
|
19603
19934
|
});
|
|
19604
19935
|
}
|
|
@@ -19626,39 +19957,104 @@ var RosterStore = class {
|
|
|
19626
19957
|
return sections;
|
|
19627
19958
|
}
|
|
19628
19959
|
enqueue(operation) {
|
|
19629
|
-
const result = this.tail.then(
|
|
19630
|
-
|
|
19960
|
+
const result = this.tail.then(() => {
|
|
19961
|
+
if (!this.available) {
|
|
19962
|
+
this.reportUnavailable();
|
|
19963
|
+
throw new RosterUnavailableError();
|
|
19964
|
+
}
|
|
19965
|
+
let value;
|
|
19631
19966
|
try {
|
|
19632
|
-
this.
|
|
19967
|
+
value = this.database.transaction((database) => {
|
|
19968
|
+
this.draft = this.readData();
|
|
19969
|
+
const before = JSON.stringify({
|
|
19970
|
+
state: this.draft.state,
|
|
19971
|
+
sections: [...this.draft.sections]
|
|
19972
|
+
});
|
|
19973
|
+
try {
|
|
19974
|
+
const outcome = operation();
|
|
19975
|
+
const after = JSON.stringify({
|
|
19976
|
+
state: this.draft.state,
|
|
19977
|
+
sections: [...this.draft.sections]
|
|
19978
|
+
});
|
|
19979
|
+
if (before !== after) database.prepare("UPDATE roster_arrangement SET body = ? WHERE singleton = 1").run(after);
|
|
19980
|
+
return outcome;
|
|
19981
|
+
} finally {
|
|
19982
|
+
this.draft = void 0;
|
|
19983
|
+
}
|
|
19984
|
+
}, ["roster"]);
|
|
19633
19985
|
} catch (error) {
|
|
19634
|
-
this.options.
|
|
19986
|
+
if (error instanceof OperationalDatabaseError && this.options.database.mode === "ready" && (error.cause instanceof RosterUnknownSectionError || error.cause instanceof RangeError)) throw error.cause;
|
|
19987
|
+
if (error instanceof OperationalDatabaseError) throw new RosterUnavailableError();
|
|
19988
|
+
throw error;
|
|
19635
19989
|
}
|
|
19990
|
+
this.notifyCommitted();
|
|
19636
19991
|
return value;
|
|
19637
19992
|
});
|
|
19638
19993
|
this.tail = result.then(() => {}, () => {});
|
|
19639
19994
|
return result;
|
|
19640
19995
|
}
|
|
19996
|
+
notifyCommitted() {
|
|
19997
|
+
try {
|
|
19998
|
+
this.options.onCommitted?.();
|
|
19999
|
+
} catch {
|
|
20000
|
+
this.warn("roster-notification initiator=host phase=observer-failed");
|
|
20001
|
+
}
|
|
20002
|
+
}
|
|
20003
|
+
warn(message) {
|
|
20004
|
+
try {
|
|
20005
|
+
this.options.warn?.(message);
|
|
20006
|
+
} catch {}
|
|
20007
|
+
}
|
|
19641
20008
|
reportUnavailable() {
|
|
19642
20009
|
if (this.warned) return;
|
|
19643
20010
|
this.warned = true;
|
|
19644
|
-
this.
|
|
20011
|
+
this.warn("roster-storage initiator=host phase=unavailable arrangement=read-only");
|
|
19645
20012
|
}
|
|
19646
|
-
|
|
19647
|
-
if (this.
|
|
20013
|
+
readData() {
|
|
20014
|
+
if (this.draft !== void 0) return this.draft;
|
|
20015
|
+
if (!this.available) {
|
|
19648
20016
|
this.reportUnavailable();
|
|
19649
20017
|
throw new RosterUnavailableError();
|
|
19650
20018
|
}
|
|
19651
|
-
|
|
19652
|
-
|
|
19653
|
-
|
|
19654
|
-
|
|
19655
|
-
|
|
20019
|
+
try {
|
|
20020
|
+
return this.database.read((database) => {
|
|
20021
|
+
const row = database.prepare("SELECT body FROM roster_arrangement WHERE singleton = 1").get();
|
|
20022
|
+
if (typeof row?.body !== "string") throw new RosterUnavailableError();
|
|
20023
|
+
const record = validateArrangement(JSON.parse(row.body));
|
|
20024
|
+
return {
|
|
20025
|
+
state: record.state,
|
|
20026
|
+
sections: new Map(record.sections)
|
|
20027
|
+
};
|
|
20028
|
+
});
|
|
20029
|
+
} catch {
|
|
19656
20030
|
throw new RosterUnavailableError();
|
|
19657
20031
|
}
|
|
19658
|
-
|
|
20032
|
+
}
|
|
20033
|
+
requireDraft() {
|
|
20034
|
+
if (this.draft === void 0) throw new RosterUnavailableError();
|
|
20035
|
+
return this.draft;
|
|
20036
|
+
}
|
|
20037
|
+
requireTable() {
|
|
20038
|
+
const sections = this.readData().sections;
|
|
20039
|
+
return {
|
|
20040
|
+
get: (id) => sections.get(id),
|
|
20041
|
+
entries: () => sections.entries(),
|
|
20042
|
+
put: (id, record) => {
|
|
20043
|
+
this.requireDraft().sections.set(id, record);
|
|
20044
|
+
},
|
|
20045
|
+
delete: (id) => this.requireDraft().sections.delete(id)
|
|
20046
|
+
};
|
|
20047
|
+
}
|
|
20048
|
+
requireGlobal() {
|
|
20049
|
+
return {
|
|
20050
|
+
get: () => this.readData().state,
|
|
20051
|
+
set: (state) => {
|
|
20052
|
+
this.requireDraft().state = state;
|
|
20053
|
+
}
|
|
20054
|
+
};
|
|
19659
20055
|
}
|
|
19660
20056
|
};
|
|
19661
|
-
function createRosterStore(options
|
|
20057
|
+
function createRosterStore(options) {
|
|
19662
20058
|
return new RosterStore(options);
|
|
19663
20059
|
}
|
|
19664
20060
|
//#endregion
|
|
@@ -21551,6 +21947,30 @@ function createBridgeMethods(deps) {
|
|
|
21551
21947
|
...humanNickname === void 0 ? {} : { humanNickname }
|
|
21552
21948
|
};
|
|
21553
21949
|
};
|
|
21950
|
+
const marketplaceCall = async (operation) => {
|
|
21951
|
+
if (deps.marketplace === void 0) return {
|
|
21952
|
+
ok: false,
|
|
21953
|
+
error: {
|
|
21954
|
+
code: "marketplace-unavailable",
|
|
21955
|
+
message: "Bot Marketplace is unavailable"
|
|
21956
|
+
}
|
|
21957
|
+
};
|
|
21958
|
+
const result = await operation(deps.marketplace);
|
|
21959
|
+
if (result.ok) return result;
|
|
21960
|
+
deps.warn?.(JSON.stringify({
|
|
21961
|
+
module: "marketplace",
|
|
21962
|
+
initiator: "client",
|
|
21963
|
+
phase: "request-refused",
|
|
21964
|
+
reason: result.code
|
|
21965
|
+
}));
|
|
21966
|
+
return {
|
|
21967
|
+
ok: false,
|
|
21968
|
+
error: {
|
|
21969
|
+
code: result.code,
|
|
21970
|
+
message: result.code
|
|
21971
|
+
}
|
|
21972
|
+
};
|
|
21973
|
+
};
|
|
21554
21974
|
const messagingCall = async (operation) => {
|
|
21555
21975
|
if (deps.externalMessaging === void 0) return {
|
|
21556
21976
|
ok: false,
|
|
@@ -22139,6 +22559,32 @@ function createBridgeMethods(deps) {
|
|
|
22139
22559
|
value: detailOf(result.record)
|
|
22140
22560
|
};
|
|
22141
22561
|
},
|
|
22562
|
+
marketplaceList(payload) {
|
|
22563
|
+
const source = asObject(payload);
|
|
22564
|
+
const query = {};
|
|
22565
|
+
for (const key of [
|
|
22566
|
+
"cursor",
|
|
22567
|
+
"q",
|
|
22568
|
+
"topic"
|
|
22569
|
+
]) {
|
|
22570
|
+
const value = parseOptional(source, key);
|
|
22571
|
+
if (!value.ok) return Promise.resolve(invalidInput(`invalid ${key}`));
|
|
22572
|
+
const trimmed = value.value?.trim();
|
|
22573
|
+
if (trimmed !== void 0 && trimmed.length > 0) query[key] = trimmed;
|
|
22574
|
+
}
|
|
22575
|
+
const sort = source["sort"];
|
|
22576
|
+
if (sort !== void 0 && sort !== "updated" && sort !== "stars") return Promise.resolve(invalidInput("invalid sort"));
|
|
22577
|
+
if (sort !== void 0) query.sort = sort;
|
|
22578
|
+
return marketplaceCall((client) => client.list(query));
|
|
22579
|
+
},
|
|
22580
|
+
marketplaceTopics() {
|
|
22581
|
+
return marketplaceCall((client) => client.topics());
|
|
22582
|
+
},
|
|
22583
|
+
marketplaceSubmit(payload) {
|
|
22584
|
+
const url = asObject(payload)["url"];
|
|
22585
|
+
if (typeof url !== "string" || url.trim().length === 0) return Promise.resolve(invalidInput("url is required"));
|
|
22586
|
+
return marketplaceCall((client) => client.submit(url.trim()));
|
|
22587
|
+
},
|
|
22142
22588
|
update(payload) {
|
|
22143
22589
|
const slug = asSlug(payload);
|
|
22144
22590
|
if (slug === void 0) return invalidInput("slug is required");
|
|
@@ -24546,6 +24992,15 @@ var BotharnessBridgeService = class extends TypertRemoteService {
|
|
|
24546
24992
|
developerModeSet(enabled) {
|
|
24547
24993
|
return unwrap(this.methods.developerModeSet({ enabled }));
|
|
24548
24994
|
}
|
|
24995
|
+
async marketplaceList(query) {
|
|
24996
|
+
return unwrapAsync(this.methods.marketplaceList(query ?? {}));
|
|
24997
|
+
}
|
|
24998
|
+
async marketplaceSubmit(url) {
|
|
24999
|
+
return unwrapAsync(this.methods.marketplaceSubmit({ url }));
|
|
25000
|
+
}
|
|
25001
|
+
async marketplaceTopics() {
|
|
25002
|
+
return unwrapAsync(this.methods.marketplaceTopics());
|
|
25003
|
+
}
|
|
24549
25004
|
};
|
|
24550
25005
|
markRemoteMethods(BotharnessBridgeService.prototype, [
|
|
24551
25006
|
"messagingDefaults",
|
|
@@ -24664,12 +25119,155 @@ markRemoteMethods(BotharnessBridgeService.prototype, [
|
|
|
24664
25119
|
"browserAccessSet",
|
|
24665
25120
|
"browserProfileSet",
|
|
24666
25121
|
"botAvatarSet",
|
|
24667
|
-
"botAppearanceSet"
|
|
25122
|
+
"botAppearanceSet",
|
|
25123
|
+
"marketplaceList",
|
|
25124
|
+
"marketplaceSubmit",
|
|
25125
|
+
"marketplaceTopics"
|
|
24668
25126
|
]);
|
|
24669
25127
|
function registerBridge(ctx, methods) {
|
|
24670
25128
|
return new BotharnessBridgeService(ctx, methods);
|
|
24671
25129
|
}
|
|
24672
25130
|
//#endregion
|
|
25131
|
+
//#region packages/core/src/marketplace/client.ts
|
|
25132
|
+
const REQUEST_TIMEOUT_MS = 15e3;
|
|
25133
|
+
function record$1(value) {
|
|
25134
|
+
return typeof value === "object" && value !== null && !Array.isArray(value) ? value : void 0;
|
|
25135
|
+
}
|
|
25136
|
+
function parseEntry(value) {
|
|
25137
|
+
const source = record$1(value);
|
|
25138
|
+
if (source === void 0) return void 0;
|
|
25139
|
+
if (![
|
|
25140
|
+
"id",
|
|
25141
|
+
"owner",
|
|
25142
|
+
"name",
|
|
25143
|
+
"fullName",
|
|
25144
|
+
"pushedAt",
|
|
25145
|
+
"htmlUrl",
|
|
25146
|
+
"cloneUrl",
|
|
25147
|
+
"defaultBranch"
|
|
25148
|
+
].every((key) => typeof source[key] === "string")) return void 0;
|
|
25149
|
+
const description = source["description"];
|
|
25150
|
+
const stars = source["stars"];
|
|
25151
|
+
const topics = source["topics"];
|
|
25152
|
+
const head = record$1(source["headCommit"]);
|
|
25153
|
+
if (description !== null && typeof description !== "string") return void 0;
|
|
25154
|
+
if (typeof stars !== "number" || !Array.isArray(topics)) return void 0;
|
|
25155
|
+
return {
|
|
25156
|
+
id: source["id"],
|
|
25157
|
+
owner: source["owner"],
|
|
25158
|
+
name: source["name"],
|
|
25159
|
+
fullName: source["fullName"],
|
|
25160
|
+
description,
|
|
25161
|
+
topics: topics.filter((topic) => typeof topic === "string"),
|
|
25162
|
+
stars,
|
|
25163
|
+
pushedAt: source["pushedAt"],
|
|
25164
|
+
htmlUrl: source["htmlUrl"],
|
|
25165
|
+
cloneUrl: source["cloneUrl"],
|
|
25166
|
+
defaultBranch: source["defaultBranch"],
|
|
25167
|
+
headCommit: head !== void 0 && typeof head["sha"] === "string" && typeof head["committedAt"] === "string" ? {
|
|
25168
|
+
sha: head["sha"],
|
|
25169
|
+
committedAt: head["committedAt"]
|
|
25170
|
+
} : null
|
|
25171
|
+
};
|
|
25172
|
+
}
|
|
25173
|
+
function parseMarketplacePage(value) {
|
|
25174
|
+
const source = record$1(value);
|
|
25175
|
+
if (source === void 0 || !Array.isArray(source["bots"])) return void 0;
|
|
25176
|
+
const bots = source["bots"].map(parseEntry);
|
|
25177
|
+
if (!bots.every((bot) => bot !== void 0)) return void 0;
|
|
25178
|
+
const nextCursor = source["nextCursor"];
|
|
25179
|
+
return typeof nextCursor === "string" ? {
|
|
25180
|
+
bots,
|
|
25181
|
+
nextCursor
|
|
25182
|
+
} : { bots };
|
|
25183
|
+
}
|
|
25184
|
+
function parseMarketplaceTopics(value) {
|
|
25185
|
+
const topics = record$1(value)?.["topics"];
|
|
25186
|
+
if (!Array.isArray(topics)) return void 0;
|
|
25187
|
+
const parsed = topics.map((item) => {
|
|
25188
|
+
const source = record$1(item);
|
|
25189
|
+
return typeof source?.["topic"] === "string" && typeof source["count"] === "number" ? {
|
|
25190
|
+
topic: source["topic"],
|
|
25191
|
+
count: source["count"]
|
|
25192
|
+
} : void 0;
|
|
25193
|
+
});
|
|
25194
|
+
return parsed.every((item) => item !== void 0) ? parsed : void 0;
|
|
25195
|
+
}
|
|
25196
|
+
function listPath(query) {
|
|
25197
|
+
const params = new URLSearchParams();
|
|
25198
|
+
if (query.sort !== void 0) params.set("sort", query.sort);
|
|
25199
|
+
if (query.q !== void 0) params.set("q", query.q);
|
|
25200
|
+
if (query.topic !== void 0) params.set("topic", query.topic);
|
|
25201
|
+
if (query.cursor !== void 0) params.set("cursor", query.cursor);
|
|
25202
|
+
const search = params.toString();
|
|
25203
|
+
return search.length === 0 ? "/v1/bots" : `/v1/bots?${search}`;
|
|
25204
|
+
}
|
|
25205
|
+
function parseMarketplaceSubmission(value) {
|
|
25206
|
+
const bot = parseEntry(record$1(value)?.["bot"]);
|
|
25207
|
+
return bot === void 0 ? void 0 : { bot };
|
|
25208
|
+
}
|
|
25209
|
+
function createMarketplaceClient(options) {
|
|
25210
|
+
const fetchImpl = options.fetchImpl ?? fetch;
|
|
25211
|
+
const baseUrl = options.baseUrl.replace(/\/+$/u, "");
|
|
25212
|
+
const timeoutMs = options.timeoutMs ?? REQUEST_TIMEOUT_MS;
|
|
25213
|
+
const request = async (path, init, parse) => {
|
|
25214
|
+
let response;
|
|
25215
|
+
try {
|
|
25216
|
+
response = await fetchImpl(`${baseUrl}${path}`, {
|
|
25217
|
+
...init,
|
|
25218
|
+
signal: AbortSignal.timeout(timeoutMs)
|
|
25219
|
+
});
|
|
25220
|
+
} catch {
|
|
25221
|
+
return {
|
|
25222
|
+
ok: false,
|
|
25223
|
+
code: "marketplace-unavailable"
|
|
25224
|
+
};
|
|
25225
|
+
}
|
|
25226
|
+
let body;
|
|
25227
|
+
try {
|
|
25228
|
+
body = await response.json();
|
|
25229
|
+
} catch {
|
|
25230
|
+
return {
|
|
25231
|
+
ok: false,
|
|
25232
|
+
code: "marketplace-unavailable"
|
|
25233
|
+
};
|
|
25234
|
+
}
|
|
25235
|
+
if (!response.ok) {
|
|
25236
|
+
const code = record$1(record$1(body)?.["error"])?.["code"];
|
|
25237
|
+
return {
|
|
25238
|
+
ok: false,
|
|
25239
|
+
code: typeof code === "string" ? code : "marketplace-unavailable"
|
|
25240
|
+
};
|
|
25241
|
+
}
|
|
25242
|
+
const value = parse(body);
|
|
25243
|
+
return value === void 0 ? {
|
|
25244
|
+
ok: false,
|
|
25245
|
+
code: "marketplace-unavailable"
|
|
25246
|
+
} : {
|
|
25247
|
+
ok: true,
|
|
25248
|
+
value
|
|
25249
|
+
};
|
|
25250
|
+
};
|
|
25251
|
+
return {
|
|
25252
|
+
list: (query) => request(listPath(query), {
|
|
25253
|
+
method: "GET",
|
|
25254
|
+
headers: { accept: "application/json" }
|
|
25255
|
+
}, parseMarketplacePage),
|
|
25256
|
+
topics: () => request("/v1/topics", {
|
|
25257
|
+
method: "GET",
|
|
25258
|
+
headers: { accept: "application/json" }
|
|
25259
|
+
}, parseMarketplaceTopics),
|
|
25260
|
+
submit: (url) => request("/v1/submissions", {
|
|
25261
|
+
method: "POST",
|
|
25262
|
+
headers: {
|
|
25263
|
+
accept: "application/json",
|
|
25264
|
+
"content-type": "application/json"
|
|
25265
|
+
},
|
|
25266
|
+
body: JSON.stringify({ url })
|
|
25267
|
+
}, parseMarketplaceSubmission)
|
|
25268
|
+
};
|
|
25269
|
+
}
|
|
25270
|
+
//#endregion
|
|
24673
25271
|
//#region packages/core/src/bots/avatar-snapshot.ts
|
|
24674
25272
|
function deriveAvatarAppearance(value) {
|
|
24675
25273
|
if (!isAvatarRecipe(value)) return void 0;
|
|
@@ -24705,39 +25303,117 @@ function createPersonaBotRegistry(options) {
|
|
|
24705
25303
|
const botDir = (slug) => join(rootDir, slug);
|
|
24706
25304
|
const botFile = (slug) => join(botDir(slug), "bot.json");
|
|
24707
25305
|
const defaultMemoryDir = (slug) => join(botDir(slug), "memory");
|
|
25306
|
+
const port = attachOperationalModule(options.database, "bot-registry");
|
|
25307
|
+
const recordSnapshot = (record) => {
|
|
25308
|
+
const result = {
|
|
25309
|
+
slug: record.slug,
|
|
25310
|
+
displayName: record.displayName,
|
|
25311
|
+
workspaces: [...record.workspaces],
|
|
25312
|
+
createdAt: record.createdAt
|
|
25313
|
+
};
|
|
25314
|
+
for (const key of [
|
|
25315
|
+
"roles",
|
|
25316
|
+
"tag",
|
|
25317
|
+
"description",
|
|
25318
|
+
"avatar",
|
|
25319
|
+
"appearance",
|
|
25320
|
+
"model",
|
|
25321
|
+
"preset",
|
|
25322
|
+
"memoryDir",
|
|
25323
|
+
"paused",
|
|
25324
|
+
"computerAccess",
|
|
25325
|
+
"browserAccess",
|
|
25326
|
+
"browserProfile"
|
|
25327
|
+
]) if (record[key] !== void 0) Object.assign(result, { [key]: record[key] });
|
|
25328
|
+
const route = (value) => ({
|
|
25329
|
+
provider: value.provider,
|
|
25330
|
+
model: value.model,
|
|
25331
|
+
...value.reasoningEffort === void 0 ? {} : { reasoningEffort: value.reasoningEffort }
|
|
25332
|
+
});
|
|
25333
|
+
const plan = record.modelPlan;
|
|
25334
|
+
if (plan !== void 0) result.modelPlan = {
|
|
25335
|
+
revision: plan.revision,
|
|
25336
|
+
sourcePresetId: plan.sourcePresetId,
|
|
25337
|
+
sourcePresetName: plan.sourcePresetName,
|
|
25338
|
+
appliedAt: plan.appliedAt,
|
|
25339
|
+
orchestrator: route(plan.orchestrator),
|
|
25340
|
+
assignmentDefault: route(plan.assignmentDefault),
|
|
25341
|
+
...plan.assignmentModels === void 0 ? {} : { assignmentModels: plan.assignmentModels.map((option) => ({
|
|
25342
|
+
provider: option.provider,
|
|
25343
|
+
model: option.model,
|
|
25344
|
+
allowedEfforts: [...option.allowedEfforts],
|
|
25345
|
+
defaultEffort: option.defaultEffort
|
|
25346
|
+
})) }
|
|
25347
|
+
};
|
|
25348
|
+
return result;
|
|
25349
|
+
};
|
|
25350
|
+
const decode = (text, slug) => {
|
|
25351
|
+
const parsed = JSON.parse(text);
|
|
25352
|
+
if (typeof parsed === "object" && parsed !== null && "appearance" in parsed && !isUsableAvatarAppearance(parsed.appearance, parsed.avatar)) delete parsed.appearance;
|
|
25353
|
+
if (!isPersonaBotRecord(parsed, slug)) throw new Error("Invalid PersonaBot Registry record");
|
|
25354
|
+
return recordSnapshot(parsed);
|
|
25355
|
+
};
|
|
24708
25356
|
const read = (slug) => {
|
|
24709
25357
|
if (!isValidSlug(slug)) return void 0;
|
|
24710
|
-
|
|
24711
|
-
|
|
24712
|
-
|
|
24713
|
-
}
|
|
24714
|
-
if (isMissing(error)) return void 0;
|
|
24715
|
-
throw error;
|
|
24716
|
-
}
|
|
24717
|
-
let parsed;
|
|
24718
|
-
try {
|
|
24719
|
-
parsed = JSON.parse(text);
|
|
24720
|
-
} catch {
|
|
24721
|
-
return;
|
|
24722
|
-
}
|
|
24723
|
-
if (typeof parsed === "object" && parsed !== null && "appearance" in parsed && !isUsableAvatarAppearance(parsed.appearance, parsed.avatar)) delete parsed.appearance;
|
|
24724
|
-
return isPersonaBotRecord(parsed, slug) ? parsed : void 0;
|
|
25358
|
+
return port.read((database) => {
|
|
25359
|
+
const row = database.prepare("SELECT body FROM persona_bots WHERE slug = ?").get(slug);
|
|
25360
|
+
return row === void 0 ? void 0 : decode(String(row["body"]), slug);
|
|
25361
|
+
});
|
|
24725
25362
|
};
|
|
24726
25363
|
const write = (record) => {
|
|
24727
|
-
|
|
24728
|
-
|
|
24729
|
-
|
|
24730
|
-
|
|
24731
|
-
|
|
24732
|
-
|
|
25364
|
+
port.transaction((database) => {
|
|
25365
|
+
database.prepare(`INSERT INTO persona_bots (slug, body) VALUES (?, ?)
|
|
25366
|
+
ON CONFLICT (slug) DO UPDATE SET body = excluded.body`).run(record.slug, JSON.stringify(recordSnapshot(record)));
|
|
25367
|
+
}, ["bot-registry"]);
|
|
25368
|
+
};
|
|
25369
|
+
const erase = (slug) => {
|
|
25370
|
+
port.transaction((database) => {
|
|
25371
|
+
database.prepare("DELETE FROM persona_bots WHERE slug = ?").run(slug);
|
|
25372
|
+
}, ["bot-registry"]);
|
|
25373
|
+
};
|
|
25374
|
+
const list = () => port.read((database) => database.prepare("SELECT slug, body FROM persona_bots").all().map((row) => decode(String(row["body"]), String(row["slug"]))).sort((left, right) => left.slug.localeCompare(right.slug)));
|
|
25375
|
+
if (options.database.mode === "ready" && !port.read((database) => database.prepare("SELECT singleton FROM persona_bots_import WHERE singleton = 1").get())) {
|
|
25376
|
+
const startedAt = performance.now();
|
|
25377
|
+
const report = (phase, count) => {
|
|
25378
|
+
try {
|
|
25379
|
+
options.onImport?.({
|
|
25380
|
+
phase,
|
|
25381
|
+
...count === void 0 ? {} : { count },
|
|
25382
|
+
durationMs: performance.now() - startedAt
|
|
25383
|
+
});
|
|
25384
|
+
} catch {}
|
|
25385
|
+
};
|
|
24733
25386
|
try {
|
|
24734
|
-
entries
|
|
24735
|
-
|
|
24736
|
-
|
|
24737
|
-
|
|
25387
|
+
let entries;
|
|
25388
|
+
try {
|
|
25389
|
+
entries = readdirSync(rootDir, { withFileTypes: true });
|
|
25390
|
+
} catch (error) {
|
|
25391
|
+
if (!isMissing(error)) throw error;
|
|
25392
|
+
entries = [];
|
|
25393
|
+
}
|
|
25394
|
+
const records = [];
|
|
25395
|
+
for (const entry of entries.filter((entry) => entry.isDirectory() && isValidSlug(entry.name))) {
|
|
25396
|
+
let text;
|
|
25397
|
+
try {
|
|
25398
|
+
text = readFileSync(botFile(entry.name), "utf8");
|
|
25399
|
+
} catch (error) {
|
|
25400
|
+
if (isMissing(error)) continue;
|
|
25401
|
+
throw error;
|
|
25402
|
+
}
|
|
25403
|
+
records.push(decode(text, entry.name));
|
|
25404
|
+
}
|
|
25405
|
+
port.transaction((database) => {
|
|
25406
|
+
if (database.prepare("SELECT slug FROM persona_bots LIMIT 1").get()) throw new Error("PersonaBot Registry target is populated without an import marker");
|
|
25407
|
+
const insert = database.prepare("INSERT INTO persona_bots (slug, body) VALUES (?, ?)");
|
|
25408
|
+
for (const record of records) insert.run(record.slug, JSON.stringify(record));
|
|
25409
|
+
database.prepare("INSERT INTO persona_bots_import (singleton, imported_at) VALUES (1, ?)").run(now().toISOString());
|
|
25410
|
+
}, ["bot-registry"]);
|
|
25411
|
+
report("complete", records.length);
|
|
25412
|
+
} catch {
|
|
25413
|
+
report("failed");
|
|
25414
|
+
throw new Error("PersonaBot Registry import failed; repair retained legacy records before retry");
|
|
24738
25415
|
}
|
|
24739
|
-
|
|
24740
|
-
};
|
|
25416
|
+
}
|
|
24741
25417
|
const memoryDirOf = (record) => record.memoryDir ?? defaultMemoryDir(record.slug);
|
|
24742
25418
|
const ensurePersonaFile = (memoryDir, body) => {
|
|
24743
25419
|
mkdirSync(memoryDir, { recursive: true });
|
|
@@ -24850,7 +25526,7 @@ function createPersonaBotRegistry(options) {
|
|
|
24850
25526
|
ok: false,
|
|
24851
25527
|
reason: cloned.code
|
|
24852
25528
|
};
|
|
24853
|
-
if (existsSync(botDir(input.slug))) return {
|
|
25529
|
+
if (read(input.slug) !== void 0 || existsSync(botDir(input.slug))) return {
|
|
24854
25530
|
ok: false,
|
|
24855
25531
|
reason: "duplicate"
|
|
24856
25532
|
};
|
|
@@ -24872,10 +25548,12 @@ function createPersonaBotRegistry(options) {
|
|
|
24872
25548
|
recursive: true,
|
|
24873
25549
|
force: true
|
|
24874
25550
|
});
|
|
24875
|
-
if (ownsBotDir && !created &&
|
|
24876
|
-
|
|
24877
|
-
|
|
24878
|
-
|
|
25551
|
+
if (ownsBotDir && !created && options.database.mode === "ready") try {
|
|
25552
|
+
if (read(input.slug) === void 0) rmSync(botDir(input.slug), {
|
|
25553
|
+
recursive: true,
|
|
25554
|
+
force: true
|
|
25555
|
+
});
|
|
25556
|
+
} catch {}
|
|
24879
25557
|
}
|
|
24880
25558
|
},
|
|
24881
25559
|
get(slug) {
|
|
@@ -24889,7 +25567,7 @@ function createPersonaBotRegistry(options) {
|
|
|
24889
25567
|
},
|
|
24890
25568
|
remove(slug, removeOptions) {
|
|
24891
25569
|
if (!isValidSlug(slug)) return false;
|
|
24892
|
-
const exists = existsSync(botDir(slug));
|
|
25570
|
+
const exists = read(slug) !== void 0 || existsSync(botDir(slug));
|
|
24893
25571
|
if (removeOptions?.purge === true) {
|
|
24894
25572
|
const removeFiles = () => {
|
|
24895
25573
|
if (exists) rmSync(botDir(slug), {
|
|
@@ -24899,10 +25577,11 @@ function createPersonaBotRegistry(options) {
|
|
|
24899
25577
|
};
|
|
24900
25578
|
if (options.onPurge === void 0) removeFiles();
|
|
24901
25579
|
else options.onPurge(slug, removeFiles);
|
|
25580
|
+
erase(slug);
|
|
24902
25581
|
return exists;
|
|
24903
25582
|
}
|
|
24904
25583
|
if (!exists) return false;
|
|
24905
|
-
|
|
25584
|
+
erase(slug);
|
|
24906
25585
|
return true;
|
|
24907
25586
|
},
|
|
24908
25587
|
memoryDirFor(slug) {
|
|
@@ -28585,6 +29264,49 @@ const BOT_HARNESS_SCHEMA_PLAN = defineSchemaPlan([
|
|
|
28585
29264
|
BEGIN SELECT RAISE(ABORT, 'Messaging defaults revisions are immutable'); END;
|
|
28586
29265
|
CREATE TRIGGER messaging_defaults_no_delete BEFORE DELETE ON messaging_default_revisions
|
|
28587
29266
|
BEGIN SELECT RAISE(ABORT, 'Messaging defaults revisions are immutable'); END;
|
|
29267
|
+
`);
|
|
29268
|
+
}
|
|
29269
|
+
},
|
|
29270
|
+
{
|
|
29271
|
+
generation: 54,
|
|
29272
|
+
module: "model-presets",
|
|
29273
|
+
description: "Own reusable Model Presets and their one-time import marker in the Profile database",
|
|
29274
|
+
migrate(database) {
|
|
29275
|
+
database.exec(`
|
|
29276
|
+
CREATE TABLE model_presets (id TEXT PRIMARY KEY, body TEXT NOT NULL CHECK (json_valid(body)));
|
|
29277
|
+
CREATE TABLE model_presets_import (
|
|
29278
|
+
singleton INTEGER PRIMARY KEY CHECK (singleton = 1), imported_at TEXT NOT NULL
|
|
29279
|
+
);
|
|
29280
|
+
`);
|
|
29281
|
+
}
|
|
29282
|
+
},
|
|
29283
|
+
{
|
|
29284
|
+
generation: 55,
|
|
29285
|
+
module: "bot-registry",
|
|
29286
|
+
description: "Own PersonaBot identities and applied plans with a durable Registry import marker",
|
|
29287
|
+
migrate(database) {
|
|
29288
|
+
database.exec(`
|
|
29289
|
+
CREATE TABLE persona_bots (slug TEXT PRIMARY KEY, body TEXT NOT NULL CHECK (json_valid(body)));
|
|
29290
|
+
CREATE TABLE persona_bots_import (
|
|
29291
|
+
singleton INTEGER PRIMARY KEY CHECK (singleton = 1), imported_at TEXT NOT NULL
|
|
29292
|
+
);
|
|
29293
|
+
`);
|
|
29294
|
+
}
|
|
29295
|
+
},
|
|
29296
|
+
{
|
|
29297
|
+
generation: 56,
|
|
29298
|
+
module: "roster",
|
|
29299
|
+
description: "Own Channel sections and roster arrangement in the Profile database",
|
|
29300
|
+
migrate(database) {
|
|
29301
|
+
database.exec(`
|
|
29302
|
+
CREATE TABLE roster_arrangement (
|
|
29303
|
+
singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
|
|
29304
|
+
body TEXT NOT NULL CHECK (json_valid(body))
|
|
29305
|
+
);
|
|
29306
|
+
CREATE TABLE roster_arrangement_import (
|
|
29307
|
+
singleton INTEGER PRIMARY KEY CHECK (singleton = 1),
|
|
29308
|
+
imported_at TEXT NOT NULL
|
|
29309
|
+
);
|
|
28588
29310
|
`);
|
|
28589
29311
|
}
|
|
28590
29312
|
}
|
|
@@ -37089,7 +37811,7 @@ var BotRuntimeImplementation = class {
|
|
|
37089
37811
|
}
|
|
37090
37812
|
if (!row || !["pending", "retryable"].includes(row.attempt_state)) return;
|
|
37091
37813
|
const active = this.#activeTurns.get(botSlug);
|
|
37092
|
-
if (row.reason === "group-mention" && this.#delivery(botSlug,
|
|
37814
|
+
if ((row.reason === "group-mention" || row.reason === "human-dm") && this.#delivery(botSlug, row.reason) === "steer" && active && this.#agents.steerOrchestrator) {
|
|
37093
37815
|
const source = this.#externalMessaging.inbound.read(botSlug, sourceEventId);
|
|
37094
37816
|
const unit = {
|
|
37095
37817
|
sourceEventId,
|
|
@@ -38467,13 +39189,16 @@ var BotRuntimeImplementation = class {
|
|
|
38467
39189
|
WHERE source_kind IN ('memory-change', 'bridge-message')
|
|
38468
39190
|
AND source_event_id IN (
|
|
38469
39191
|
SELECT source_event_id FROM inbox_admissions
|
|
38470
|
-
WHERE reason IN ('memory-change', 'group-mention', 'group-ordinary') AND attempt_state = 'retryable'
|
|
39192
|
+
WHERE reason IN ('memory-change', 'group-mention', 'group-ordinary', 'human-dm') AND attempt_state = 'retryable'
|
|
38471
39193
|
AND side_effect_started_at IS NULL
|
|
38472
39194
|
)
|
|
38473
39195
|
`).run();
|
|
38474
39196
|
database.prepare(`
|
|
38475
39197
|
UPDATE inbox_admissions SET observed_at = NULL
|
|
38476
|
-
WHERE reason IN ('memory-change', 'group-mention', 'group-ordinary')
|
|
39198
|
+
WHERE (reason IN ('memory-change', 'group-mention', 'group-ordinary') OR
|
|
39199
|
+
(reason = 'human-dm' AND source_event_id IN (
|
|
39200
|
+
SELECT source_event_id FROM source_events WHERE source_kind = 'bridge-message'
|
|
39201
|
+
))) AND attempt_state = 'retryable'
|
|
38477
39202
|
AND side_effect_started_at IS NULL
|
|
38478
39203
|
`).run();
|
|
38479
39204
|
}, ["source-event", "bot-inbox"]);
|
|
@@ -39779,7 +40504,7 @@ var BotRuntimeImplementation = class {
|
|
|
39779
40504
|
AND a.attempt_state IN ('pending', 'retryable') AND a.observed_at IS NULL
|
|
39780
40505
|
WINDOW policy AS (PARTITION BY g.id, a.wake_policy_revision, a.source_policy_revision, a.external_default_revision, a.reason, CASE WHEN json_extract(tp.body, '$.mode') = 'follow' THEN tp.thread_id ELSE '' END, CASE WHEN json_extract(tp.body, '$.mode') = 'follow' THEN tp.revision ELSE 0 END)
|
|
39781
40506
|
) SELECT source_event_id, body, created_at, attempt_state FROM pending
|
|
39782
|
-
WHERE reason = 'group-mention' OR (reason = 'group-ordinary' AND (
|
|
40507
|
+
WHERE reason = 'group-mention' OR (reason = 'human-dm' AND wake_mode = 'all') OR (reason = 'group-ordinary' AND (
|
|
39783
40508
|
wake_mode = 'all' OR (wake_mode = 'digest' AND (pending_count >= wake_count OR
|
|
39784
40509
|
(julianday(?) - julianday(first_at)) * 86400000 >= wake_interval_ms)) OR
|
|
39785
40510
|
(? = 1 AND has_mention = 1 AND wake_mode IN ('digest', 'mentions'))))
|
|
@@ -39907,7 +40632,7 @@ var BotRuntimeImplementation = class {
|
|
|
39907
40632
|
this.#database.transaction((db) => {
|
|
39908
40633
|
for (const id of ids) if (db.prepare(`UPDATE inbox_admissions SET observed_at = COALESCE(observed_at, ?),
|
|
39909
40634
|
side_effect_started_at = COALESCE(side_effect_started_at, ?), attempt_state = 'running', last_error = NULL
|
|
39910
|
-
WHERE bot_slug = ? AND source_event_id = ? AND reason IN ('group-mention', 'group-ordinary')
|
|
40635
|
+
WHERE bot_slug = ? AND source_event_id = ? AND reason IN ('group-mention', 'group-ordinary', 'human-dm')
|
|
39911
40636
|
AND attempt_state IN ('pending', 'retryable') RETURNING source_event_id`).get(at, at, botSlug, id)) readAdmissions.add(id);
|
|
39912
40637
|
}, ["bot-inbox"]);
|
|
39913
40638
|
}
|
|
@@ -39963,7 +40688,7 @@ var BotRuntimeImplementation = class {
|
|
|
39963
40688
|
this.#database.transaction((database) => database.prepare(`
|
|
39964
40689
|
UPDATE inbox_admissions
|
|
39965
40690
|
SET side_effect_started_at = COALESCE(side_effect_started_at, ?)
|
|
39966
|
-
WHERE bot_slug = ? AND reason IN ('assignment-report', 'assignment-lifecycle', 'memory-change', 'group-mention', 'group-ordinary')
|
|
40691
|
+
WHERE bot_slug = ? AND reason IN ('assignment-report', 'assignment-lifecycle', 'memory-change', 'group-mention', 'group-ordinary', 'human-dm')
|
|
39967
40692
|
AND attempt_state = 'running'
|
|
39968
40693
|
AND source_event_id IN (${placeholders})
|
|
39969
40694
|
`).run(this.#now().toISOString(), botSlug, ...sourceEventIds), ["bot-inbox"]);
|
|
@@ -39982,7 +40707,7 @@ var BotRuntimeImplementation = class {
|
|
|
39982
40707
|
WHEN side_effect_started_at IS NULL THEN 'retryable'
|
|
39983
40708
|
ELSE 'needs-repair'
|
|
39984
40709
|
END
|
|
39985
|
-
WHERE reason IN ('assignment-report', 'assignment-lifecycle', 'memory-change', 'group-mention', 'group-ordinary')
|
|
40710
|
+
WHERE reason IN ('assignment-report', 'assignment-lifecycle', 'memory-change', 'group-mention', 'group-ordinary', 'human-dm')
|
|
39986
40711
|
AND attempt_state IN ('pending', 'retryable', 'running')
|
|
39987
40712
|
AND (? IS NULL OR bot_slug = ?)
|
|
39988
40713
|
AND source_event_id IN (${placeholders})
|
|
@@ -39994,7 +40719,7 @@ var BotRuntimeImplementation = class {
|
|
|
39994
40719
|
const placeholders = sourceEventIds.map(() => "?").join(", ");
|
|
39995
40720
|
this.#database.transaction((database) => database.prepare(`
|
|
39996
40721
|
UPDATE inbox_admissions SET attempt_state = 'handled', handled_at = ?
|
|
39997
|
-
WHERE reason IN ('assignment-report', 'assignment-lifecycle', 'memory-change', 'group-mention', 'group-ordinary')
|
|
40722
|
+
WHERE reason IN ('assignment-report', 'assignment-lifecycle', 'memory-change', 'group-mention', 'group-ordinary', 'human-dm')
|
|
39998
40723
|
AND attempt_state = 'running'
|
|
39999
40724
|
AND (? IS NULL OR bot_slug = ?)
|
|
40000
40725
|
AND source_event_id IN (${placeholders})
|
|
@@ -48176,6 +48901,7 @@ function handleCompactionEvent(sink, sessionId, eventType) {
|
|
|
48176
48901
|
}
|
|
48177
48902
|
}
|
|
48178
48903
|
const DEFAULT_AGENT_PRESET = "standard";
|
|
48904
|
+
const DEFAULT_MARKETPLACE_URL = "https://market.botharness.ai";
|
|
48179
48905
|
const DEFAULT_CONFIG = {
|
|
48180
48906
|
enabled: true,
|
|
48181
48907
|
agentPreset: DEFAULT_AGENT_PRESET
|
|
@@ -48183,7 +48909,8 @@ const DEFAULT_CONFIG = {
|
|
|
48183
48909
|
const Config = Schema.object({
|
|
48184
48910
|
enabled: Schema.boolean().default(DEFAULT_CONFIG.enabled).description("启用 BotHarness core"),
|
|
48185
48911
|
activityDetailConsumers: Schema.array(Schema.string()).default([]).description("Explicitly trusted Host Plugin names allowed to read Activity Tool details"),
|
|
48186
|
-
agentPreset: Schema.string().default(DEFAULT_AGENT_PRESET).description("PersonaBot 会话加入的 DSH agent preset(提供 file/Shell/grep 等普通工具)")
|
|
48912
|
+
agentPreset: Schema.string().default(DEFAULT_AGENT_PRESET).description("PersonaBot 会话加入的 DSH agent preset(提供 file/Shell/grep 等普通工具)"),
|
|
48913
|
+
marketplaceUrl: Schema.string().default(DEFAULT_MARKETPLACE_URL).description("Bot Marketplace 服务地址")
|
|
48187
48914
|
});
|
|
48188
48915
|
function unavailableAgentAdapter() {
|
|
48189
48916
|
const unavailable = () => Promise.reject(/* @__PURE__ */ new Error("BotHarness Agent runtime is unavailable outside a DSH Host"));
|
|
@@ -48199,45 +48926,65 @@ function unavailableAgentAdapter() {
|
|
|
48199
48926
|
function createCore(options = {}) {
|
|
48200
48927
|
const dshHome = options.dshHome ?? resolveDshHome();
|
|
48201
48928
|
const rootDir = join(dshHome, "botharness", "bots");
|
|
48929
|
+
const operationalDatabase = mountOperationalDatabase({
|
|
48930
|
+
dshHome,
|
|
48931
|
+
schemaPlan: BOT_HARNESS_SCHEMA_PLAN
|
|
48932
|
+
});
|
|
48202
48933
|
let usage;
|
|
48203
|
-
|
|
48204
|
-
|
|
48205
|
-
|
|
48206
|
-
|
|
48207
|
-
|
|
48208
|
-
|
|
48209
|
-
|
|
48934
|
+
let registry;
|
|
48935
|
+
let modelPresets;
|
|
48936
|
+
let roster;
|
|
48937
|
+
let live;
|
|
48938
|
+
try {
|
|
48939
|
+
registry = createPersonaBotRegistry({
|
|
48940
|
+
rootDir,
|
|
48941
|
+
database: operationalDatabase,
|
|
48942
|
+
onImport: (event) => options.warn?.(`bot-registry-import initiator=host-startup phase=${event.phase} count=${event.count ?? 0} durationMs=${Math.round(event.durationMs)}`),
|
|
48943
|
+
onDisplayNameChanged: () => {
|
|
48944
|
+
try {
|
|
48945
|
+
live?.publishRosterCommitted();
|
|
48946
|
+
} catch {
|
|
48947
|
+
options.warn?.("bot-name-publication-failed");
|
|
48948
|
+
}
|
|
48949
|
+
},
|
|
48950
|
+
onPurge: (slug, removeFiles) => {
|
|
48951
|
+
if (usage === void 0) throw new Error("Usage purge requires a ready operational database");
|
|
48952
|
+
usage.purgeBot(slug, removeFiles);
|
|
48953
|
+
},
|
|
48954
|
+
cloneMemory: (destination, url) => cloneMemoryRepository({
|
|
48955
|
+
destination,
|
|
48956
|
+
url
|
|
48957
|
+
}),
|
|
48958
|
+
initializeMemory: (memoryDir) => {
|
|
48959
|
+
const repository = ensureMemoryRepository({ memoryDir });
|
|
48960
|
+
return repository.ok ? { ok: true } : {
|
|
48961
|
+
ok: false,
|
|
48962
|
+
...repository.code === "git-not-found" ? { code: "git-not-found" } : {},
|
|
48963
|
+
message: `${repository.code}: ${repository.message}`
|
|
48964
|
+
};
|
|
48210
48965
|
}
|
|
48211
|
-
}
|
|
48212
|
-
|
|
48213
|
-
|
|
48214
|
-
|
|
48215
|
-
|
|
48216
|
-
|
|
48217
|
-
|
|
48218
|
-
|
|
48219
|
-
|
|
48220
|
-
|
|
48221
|
-
|
|
48222
|
-
|
|
48223
|
-
|
|
48224
|
-
|
|
48225
|
-
|
|
48226
|
-
};
|
|
48227
|
-
}
|
|
48228
|
-
});
|
|
48229
|
-
const modelPresets = createModelPresetStore(join(dshHome, "botharness"));
|
|
48966
|
+
});
|
|
48967
|
+
roster = createRosterStore({
|
|
48968
|
+
database: operationalDatabase,
|
|
48969
|
+
warn: options.warn,
|
|
48970
|
+
onCommitted: () => live?.publishRosterCommitted()
|
|
48971
|
+
});
|
|
48972
|
+
modelPresets = createModelPresetStore({
|
|
48973
|
+
rootDir: join(dshHome, "botharness"),
|
|
48974
|
+
database: operationalDatabase,
|
|
48975
|
+
onImport: (event) => options.warn?.(`model-presets-import initiator=host-startup phase=${event.phase} count=${event.count ?? 0} durationMs=${Math.round(event.durationMs)}`)
|
|
48976
|
+
});
|
|
48977
|
+
} catch (error) {
|
|
48978
|
+
operationalDatabase.close();
|
|
48979
|
+
throw error;
|
|
48980
|
+
}
|
|
48230
48981
|
const states = createBotStateTracker();
|
|
48231
|
-
let live;
|
|
48232
48982
|
let runtime;
|
|
48233
48983
|
const attachments = createAttachmentStore({ rootDir: join(dshHome, "botharness", "attachments") });
|
|
48234
|
-
const operationalDatabase = mountOperationalDatabase({
|
|
48235
|
-
dshHome,
|
|
48236
|
-
schemaPlan: BOT_HARNESS_SCHEMA_PLAN
|
|
48237
|
-
});
|
|
48238
48984
|
const sourcePolicy = createBotSourcePolicyStore(attachOperationalModule(operationalDatabase, "bot-inbox"));
|
|
48239
48985
|
const externalMessaging = createOutboundMessaging({
|
|
48240
48986
|
onDefaultsChanged: () => live?.publishRosterCommitted(),
|
|
48987
|
+
onReceptionChanged: () => live?.publishRosterCommitted(),
|
|
48241
48988
|
attachments,
|
|
48242
48989
|
database: attachOperationalModule(operationalDatabase, "messaging"),
|
|
48243
48990
|
sourcePolicy,
|
|
@@ -48302,6 +49049,7 @@ function createCore(options = {}) {
|
|
|
48302
49049
|
const refreshDurableAttention = () => states.replaceDurableAttention(humanAttention.durableAttention().filter((row) => registry.get(row.botSlug) !== void 0));
|
|
48303
49050
|
refreshDurableAttention();
|
|
48304
49051
|
operationalDatabase.subscribe(({ topics }) => {
|
|
49052
|
+
if (topics.some((topic) => topic === "bindings" || topic === "grants")) live?.publishRosterCommitted();
|
|
48305
49053
|
if (topics.some((topic) => [
|
|
48306
49054
|
"assignments",
|
|
48307
49055
|
"source-event",
|
|
@@ -48409,10 +49157,7 @@ function createCore(options = {}) {
|
|
|
48409
49157
|
humanAttentionDecisions,
|
|
48410
49158
|
attachments,
|
|
48411
49159
|
live,
|
|
48412
|
-
roster
|
|
48413
|
-
warn: options.warn,
|
|
48414
|
-
onCommitted: () => live?.publishRosterCommitted()
|
|
48415
|
-
}),
|
|
49160
|
+
roster,
|
|
48416
49161
|
runtime
|
|
48417
49162
|
};
|
|
48418
49163
|
}
|
|
@@ -48484,6 +49229,7 @@ function apply(ctx, config) {
|
|
|
48484
49229
|
});
|
|
48485
49230
|
publishDraft = (event) => core.live.publishDraft(event);
|
|
48486
49231
|
ctx.effect(() => () => core.operationalDatabase.close(), "botharness: operational database");
|
|
49232
|
+
ctx.effect(() => () => core.roster.detach(), "botharness: roster");
|
|
48487
49233
|
ctx.effect(() => {
|
|
48488
49234
|
const controller = new AbortController();
|
|
48489
49235
|
if (core.operationalDatabase.mode === "ready") core.channels.migrateAttachments?.(controller.signal).catch(() => {
|
|
@@ -48499,7 +49245,8 @@ function apply(ctx, config) {
|
|
|
48499
49245
|
for (const platform of [
|
|
48500
49246
|
"feishu",
|
|
48501
49247
|
"slack",
|
|
48502
|
-
"discord"
|
|
49248
|
+
"discord",
|
|
49249
|
+
"weixin"
|
|
48503
49250
|
]) {
|
|
48504
49251
|
const provider = createDshImProvider(child.get("dshIm"), platform);
|
|
48505
49252
|
if (provider !== void 0) child.effect(() => core.externalMessaging.register(provider));
|
|
@@ -48702,6 +49449,7 @@ function apply(ctx, config) {
|
|
|
48702
49449
|
userQuestions,
|
|
48703
49450
|
toolRules: core.toolRules,
|
|
48704
49451
|
assignmentAccess: core.assignmentAccess,
|
|
49452
|
+
marketplace: createMarketplaceClient({ baseUrl: config.marketplaceUrl ?? "https://market.botharness.ai" }),
|
|
48705
49453
|
developerMode: { set: (enabled) => developerModeTarget.gate?.set(enabled) },
|
|
48706
49454
|
computerAccess: { changed: (slug) => {
|
|
48707
49455
|
(ctx.get("botharnessComputerTools")?.reconcileBot?.(slug))?.catch((error) => {
|
|
@@ -48847,10 +49595,9 @@ function apply(ctx, config) {
|
|
|
48847
49595
|
});
|
|
48848
49596
|
}
|
|
48849
49597
|
ctx.inject(["storageDomain"], (storageCtx) => {
|
|
48850
|
-
core.roster.attach(storageCtx.storageDomain).catch((
|
|
48851
|
-
ctx.logger.warn(
|
|
49598
|
+
core.roster.attach(storageCtx.storageDomain).catch(() => {
|
|
49599
|
+
ctx.logger.warn("roster-import initiator=host-startup phase=unavailable arrangement=read-only");
|
|
48852
49600
|
});
|
|
48853
|
-
storageCtx.effect(() => () => core.roster.detach(), "botharness: roster domain");
|
|
48854
49601
|
});
|
|
48855
49602
|
ctx.systemPrompt.section({
|
|
48856
49603
|
name: "botharness:persona",
|