@trops/dash-core 0.1.626 → 0.1.628
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/electron/index.js +426 -2
- package/dist/electron/index.js.map +1 -1
- package/package.json +1 -1
package/dist/electron/index.js
CHANGED
|
@@ -73683,6 +73683,7 @@ const Store = require$$0$5;
|
|
|
73683
73683
|
function createElectronHost$1() {
|
|
73684
73684
|
const store = new Store({ name: "dash-bots" });
|
|
73685
73685
|
const budgetStore = new Store({ name: "dash-bot-budgets" });
|
|
73686
|
+
const memoryStore = new Store({ name: "dash-bot-memory" });
|
|
73686
73687
|
const botsRoot = path$3.join(app$2.getPath("userData"), "bots");
|
|
73687
73688
|
|
|
73688
73689
|
return {
|
|
@@ -73700,6 +73701,11 @@ function createElectronHost$1() {
|
|
|
73700
73701
|
budgetStore.get("store", { budgets: {}, spend: {}, overrides: {} }),
|
|
73701
73702
|
write: (obj) => budgetStore.set("store", obj),
|
|
73702
73703
|
},
|
|
73704
|
+
// Separate blob for scoped bot memory (P1: FR-010).
|
|
73705
|
+
memoryPersistence: {
|
|
73706
|
+
read: () => memoryStore.get("store", { scopes: {} }),
|
|
73707
|
+
write: (obj) => memoryStore.set("store", obj),
|
|
73708
|
+
},
|
|
73703
73709
|
};
|
|
73704
73710
|
}
|
|
73705
73711
|
|
|
@@ -73808,11 +73814,25 @@ function validateBotDefinition$1(def) {
|
|
|
73808
73814
|
if (!WHILE_PAUSED.has(def.whilePaused)) {
|
|
73809
73815
|
errors.push(`whilePaused must be one of ${[...WHILE_PAUSED].join(", ")}`);
|
|
73810
73816
|
}
|
|
73811
|
-
for (const field of [
|
|
73812
|
-
|
|
73817
|
+
for (const field of [
|
|
73818
|
+
"mcpServers",
|
|
73819
|
+
"allowedTools",
|
|
73820
|
+
"schedules",
|
|
73821
|
+
"subscriptions",
|
|
73822
|
+
]) {
|
|
73823
|
+
if (def[field] !== undefined && !Array.isArray(def[field])) {
|
|
73813
73824
|
errors.push(`${field} must be an array`);
|
|
73814
73825
|
}
|
|
73815
73826
|
}
|
|
73827
|
+
// Each subscription must name an eventType (PRD FR-009).
|
|
73828
|
+
if (Array.isArray(def.subscriptions)) {
|
|
73829
|
+
for (const sub of def.subscriptions) {
|
|
73830
|
+
if (!sub || typeof sub.eventType !== "string" || !sub.eventType.trim()) {
|
|
73831
|
+
errors.push("each subscription must have a non-empty eventType");
|
|
73832
|
+
break;
|
|
73833
|
+
}
|
|
73834
|
+
}
|
|
73835
|
+
}
|
|
73816
73836
|
return { valid: errors.length === 0, errors };
|
|
73817
73837
|
}
|
|
73818
73838
|
|
|
@@ -74777,6 +74797,7 @@ var engines$1 = { getEngine, listEngines, registerEngine };
|
|
|
74777
74797
|
* botId: string,
|
|
74778
74798
|
* allowedTools?: string[],
|
|
74779
74799
|
* mcpServers?: string[],
|
|
74800
|
+
* internalServers?: string[],
|
|
74780
74801
|
* approvalPolicy?: "ask" | "allow",
|
|
74781
74802
|
* resolveServer: (toolName: string) => (string | null),
|
|
74782
74803
|
* createApproval: (request: object) => { id: string, promise: Promise<any> },
|
|
@@ -74792,6 +74813,7 @@ function createRequestPermission$1(ctx) {
|
|
|
74792
74813
|
botId,
|
|
74793
74814
|
allowedTools = [],
|
|
74794
74815
|
mcpServers = [],
|
|
74816
|
+
internalServers = [],
|
|
74795
74817
|
resolveServer,
|
|
74796
74818
|
createApproval,
|
|
74797
74819
|
audit = () => {},
|
|
@@ -74840,6 +74862,16 @@ function createRequestPermission$1(ctx) {
|
|
|
74840
74862
|
|
|
74841
74863
|
const serverName = resolveServer(toolName);
|
|
74842
74864
|
|
|
74865
|
+
// 1b. Internal Bot Factory tools (e.g. bot-memory) are the bot's own
|
|
74866
|
+
// sandbox — auto-allowed, never a consent prompt or external action.
|
|
74867
|
+
if (serverName && internalServers.includes(serverName)) {
|
|
74868
|
+
return record(
|
|
74869
|
+
toolName,
|
|
74870
|
+
{ allow: true },
|
|
74871
|
+
{ outcome: "internal", serverName },
|
|
74872
|
+
);
|
|
74873
|
+
}
|
|
74874
|
+
|
|
74843
74875
|
// 2. A tool whose server the bot was never configured with is denied
|
|
74844
74876
|
// without prompting — it's not a consent gap, it's out of scope.
|
|
74845
74877
|
if (!serverName || !mcpServers.includes(serverName)) {
|
|
@@ -74959,6 +74991,9 @@ let BotRunner$1 = class BotRunner {
|
|
|
74959
74991
|
this._makeRequestPermission =
|
|
74960
74992
|
deps.makeRequestPermission || createRequestPermission;
|
|
74961
74993
|
this._gate = deps.gate; // forwarded to PermissionGate ctx (optional)
|
|
74994
|
+
// Virtual servers whose tools are auto-allowed (e.g. "bot-memory") — the
|
|
74995
|
+
// bot's own sandbox, not external actions. Forwarded to the gate ctx.
|
|
74996
|
+
this._internalServers = deps.internalServers || [];
|
|
74962
74997
|
this._isPaused = deps.isPaused || (() => false);
|
|
74963
74998
|
// Called after each run with the run's token usage so budgets (Slice 5)
|
|
74964
74999
|
// can accrue cost. Optional — default no-op keeps the runner portable.
|
|
@@ -75030,6 +75065,7 @@ let BotRunner$1 = class BotRunner {
|
|
|
75030
75065
|
workspaceId: bot.workspaceId,
|
|
75031
75066
|
allowedTools: bot.allowedTools || [],
|
|
75032
75067
|
mcpServers: bot.mcpServers || [],
|
|
75068
|
+
internalServers: this._internalServers,
|
|
75033
75069
|
resolveServer,
|
|
75034
75070
|
createApproval: (request) => this._approvals.create(request),
|
|
75035
75071
|
audit: this._audit,
|
|
@@ -75491,6 +75527,319 @@ let PauseController$1 = class PauseController {
|
|
|
75491
75527
|
|
|
75492
75528
|
var PauseController_1 = PauseController$1;
|
|
75493
75529
|
|
|
75530
|
+
/**
|
|
75531
|
+
* BotMemory.js
|
|
75532
|
+
*
|
|
75533
|
+
* Durable, scoped key-value memory for bots (PRD FR-010 / FR-012, US-012/015):
|
|
75534
|
+
* a bot can remember facts across runs. Values are namespaced by scope
|
|
75535
|
+
* (`workspace` — isolated per workspace — or `global` — shared across a
|
|
75536
|
+
* principal's bots) and carry a monotonic `version` + `updatedAt` so a later
|
|
75537
|
+
* slice can add history/optimistic concurrency without a data migration.
|
|
75538
|
+
*
|
|
75539
|
+
* Portable (NFR-006): the durable blob is injected as `persistence` (same shape
|
|
75540
|
+
* as BotStore/BudgetController), so this loads and runs under plain Node. The
|
|
75541
|
+
* Electron wiring (electron-store) lives in host.js. A KV store needs no SQL for
|
|
75542
|
+
* v1; SQLite can later slot in behind this exact interface if volume demands.
|
|
75543
|
+
*/
|
|
75544
|
+
|
|
75545
|
+
const VALID_SCOPES = ["workspace", "global"];
|
|
75546
|
+
|
|
75547
|
+
/** Bucket key for a (scope, id) pair. Global collapses all ids to one bucket. */
|
|
75548
|
+
function bucketKey(scope, id) {
|
|
75549
|
+
return scope === "global" ? "global" : `workspace:${id || "default"}`;
|
|
75550
|
+
}
|
|
75551
|
+
|
|
75552
|
+
let BotMemory$1 = class BotMemory {
|
|
75553
|
+
/**
|
|
75554
|
+
* @param {{ persistence: { read: () => object, write: (o: object) => void },
|
|
75555
|
+
* clock?: { now: () => string } }} deps
|
|
75556
|
+
*/
|
|
75557
|
+
constructor({ persistence, clock } = {}) {
|
|
75558
|
+
if (!persistence) throw new Error("BotMemory: persistence is required");
|
|
75559
|
+
this._persistence = persistence;
|
|
75560
|
+
this._now = (clock && clock.now) || (() => new Date().toISOString());
|
|
75561
|
+
}
|
|
75562
|
+
|
|
75563
|
+
_read() {
|
|
75564
|
+
const blob = this._persistence.read() || {};
|
|
75565
|
+
return blob.scopes ? blob : { scopes: {} };
|
|
75566
|
+
}
|
|
75567
|
+
|
|
75568
|
+
_write(blob) {
|
|
75569
|
+
this._persistence.write(blob);
|
|
75570
|
+
}
|
|
75571
|
+
|
|
75572
|
+
/** @returns {*} the stored value, or undefined if absent. */
|
|
75573
|
+
get(scope, id, key) {
|
|
75574
|
+
const bucket = this._read().scopes[bucketKey(scope, id)] || {};
|
|
75575
|
+
const entry = bucket[key];
|
|
75576
|
+
return entry ? entry.value : undefined;
|
|
75577
|
+
}
|
|
75578
|
+
|
|
75579
|
+
/** Store (or overwrite) a value; bumps version. @returns the stored entry. */
|
|
75580
|
+
set(scope, id, key, value) {
|
|
75581
|
+
const blob = this._read();
|
|
75582
|
+
const bk = bucketKey(scope, id);
|
|
75583
|
+
const bucket = blob.scopes[bk] || (blob.scopes[bk] = {});
|
|
75584
|
+
const prev = bucket[key];
|
|
75585
|
+
const entry = {
|
|
75586
|
+
value,
|
|
75587
|
+
version: prev ? prev.version + 1 : 1,
|
|
75588
|
+
updatedAt: this._now(),
|
|
75589
|
+
};
|
|
75590
|
+
bucket[key] = entry;
|
|
75591
|
+
this._write(blob);
|
|
75592
|
+
return entry;
|
|
75593
|
+
}
|
|
75594
|
+
|
|
75595
|
+
/** @returns {Array<{key,value,version,updatedAt}>} entries, optionally prefixed. */
|
|
75596
|
+
list(scope, id, prefix) {
|
|
75597
|
+
const bucket = this._read().scopes[bucketKey(scope, id)] || {};
|
|
75598
|
+
return Object.keys(bucket)
|
|
75599
|
+
.filter((k) => !prefix || k.startsWith(prefix))
|
|
75600
|
+
.sort()
|
|
75601
|
+
.map((k) => ({
|
|
75602
|
+
key: k,
|
|
75603
|
+
value: bucket[k].value,
|
|
75604
|
+
version: bucket[k].version,
|
|
75605
|
+
updatedAt: bucket[k].updatedAt,
|
|
75606
|
+
}));
|
|
75607
|
+
}
|
|
75608
|
+
|
|
75609
|
+
/** @returns {boolean} true if a value was removed. */
|
|
75610
|
+
delete(scope, id, key) {
|
|
75611
|
+
const blob = this._read();
|
|
75612
|
+
const bucket = blob.scopes[bucketKey(scope, id)];
|
|
75613
|
+
if (!bucket || !(key in bucket)) return false;
|
|
75614
|
+
delete bucket[key];
|
|
75615
|
+
this._write(blob);
|
|
75616
|
+
return true;
|
|
75617
|
+
}
|
|
75618
|
+
};
|
|
75619
|
+
|
|
75620
|
+
var BotMemory_1 = { BotMemory: BotMemory$1, VALID_SCOPES };
|
|
75621
|
+
|
|
75622
|
+
/**
|
|
75623
|
+
* memoryTools.js
|
|
75624
|
+
*
|
|
75625
|
+
* Exposes BotMemory to bots as in-process tools (PRD FR-010): memory_get /
|
|
75626
|
+
* memory_set / memory_list / memory_delete. These are served under a virtual
|
|
75627
|
+
* "bot-memory" server that the bot PermissionGate auto-allows (it's the bot's
|
|
75628
|
+
* own sandbox, not an external action), so no consent prompt is required.
|
|
75629
|
+
*
|
|
75630
|
+
* Portable (NFR-006): no Electron. botController wires a BotMemory instance and
|
|
75631
|
+
* the calling bot's scope, and routes MEMORY_SERVER tool calls here. The result
|
|
75632
|
+
* shape matches the tool-loop engine's executeTool contract: { text, isError }.
|
|
75633
|
+
*/
|
|
75634
|
+
|
|
75635
|
+
const MEMORY_SERVER$1 = "bot-memory";
|
|
75636
|
+
|
|
75637
|
+
const SCOPE_PROP = {
|
|
75638
|
+
type: "string",
|
|
75639
|
+
enum: ["workspace", "global"],
|
|
75640
|
+
description:
|
|
75641
|
+
"Which memory scope to use: 'workspace' (default, isolated to this bot's workspace) or 'global' (shared across your bots).",
|
|
75642
|
+
};
|
|
75643
|
+
|
|
75644
|
+
const MEMORY_TOOLS$1 = [
|
|
75645
|
+
{
|
|
75646
|
+
name: "memory_set",
|
|
75647
|
+
description:
|
|
75648
|
+
"Store a value in your durable memory under a key. Persists across runs so you can recall it later.",
|
|
75649
|
+
inputSchema: {
|
|
75650
|
+
type: "object",
|
|
75651
|
+
properties: {
|
|
75652
|
+
key: { type: "string", description: "Key to store the value under." },
|
|
75653
|
+
value: { description: "The value to store (any JSON)." },
|
|
75654
|
+
scope: SCOPE_PROP,
|
|
75655
|
+
},
|
|
75656
|
+
required: ["key", "value"],
|
|
75657
|
+
},
|
|
75658
|
+
},
|
|
75659
|
+
{
|
|
75660
|
+
name: "memory_get",
|
|
75661
|
+
description:
|
|
75662
|
+
"Retrieve a value previously stored with memory_set. Returns the value, or a note if nothing is stored.",
|
|
75663
|
+
inputSchema: {
|
|
75664
|
+
type: "object",
|
|
75665
|
+
properties: {
|
|
75666
|
+
key: { type: "string", description: "Key to look up." },
|
|
75667
|
+
scope: SCOPE_PROP,
|
|
75668
|
+
},
|
|
75669
|
+
required: ["key"],
|
|
75670
|
+
},
|
|
75671
|
+
},
|
|
75672
|
+
{
|
|
75673
|
+
name: "memory_list",
|
|
75674
|
+
description:
|
|
75675
|
+
"List the keys currently in your memory, optionally filtered by a key prefix.",
|
|
75676
|
+
inputSchema: {
|
|
75677
|
+
type: "object",
|
|
75678
|
+
properties: {
|
|
75679
|
+
prefix: {
|
|
75680
|
+
type: "string",
|
|
75681
|
+
description: "Only list keys with this prefix.",
|
|
75682
|
+
},
|
|
75683
|
+
scope: SCOPE_PROP,
|
|
75684
|
+
},
|
|
75685
|
+
},
|
|
75686
|
+
},
|
|
75687
|
+
{
|
|
75688
|
+
name: "memory_delete",
|
|
75689
|
+
description: "Delete a key from your memory.",
|
|
75690
|
+
inputSchema: {
|
|
75691
|
+
type: "object",
|
|
75692
|
+
properties: {
|
|
75693
|
+
key: { type: "string", description: "Key to delete." },
|
|
75694
|
+
scope: SCOPE_PROP,
|
|
75695
|
+
},
|
|
75696
|
+
required: ["key"],
|
|
75697
|
+
},
|
|
75698
|
+
},
|
|
75699
|
+
];
|
|
75700
|
+
|
|
75701
|
+
function ok$1(text) {
|
|
75702
|
+
return { text, isError: false };
|
|
75703
|
+
}
|
|
75704
|
+
function fail$1(text) {
|
|
75705
|
+
return { text, isError: true };
|
|
75706
|
+
}
|
|
75707
|
+
|
|
75708
|
+
/**
|
|
75709
|
+
* Execute a memory_* tool call in-process.
|
|
75710
|
+
* @param {import("./BotMemory").BotMemory} memory
|
|
75711
|
+
* @param {{ workspaceId?: string }} ctx the calling bot's scope context
|
|
75712
|
+
* @param {string} toolName
|
|
75713
|
+
* @param {object} args
|
|
75714
|
+
* @returns {{ text: string, isError: boolean }}
|
|
75715
|
+
*/
|
|
75716
|
+
function handleMemoryTool$1(memory, ctx, toolName, args = {}) {
|
|
75717
|
+
const scope = args && args.scope === "global" ? "global" : "workspace";
|
|
75718
|
+
const id = ctx && ctx.workspaceId;
|
|
75719
|
+
try {
|
|
75720
|
+
switch (toolName) {
|
|
75721
|
+
case "memory_set": {
|
|
75722
|
+
if (!args || !args.key) return fail$1("memory_set requires a 'key'.");
|
|
75723
|
+
const entry = memory.set(scope, id, args.key, args.value);
|
|
75724
|
+
return ok$1(`Stored '${args.key}' (${scope}, v${entry.version}).`);
|
|
75725
|
+
}
|
|
75726
|
+
case "memory_get": {
|
|
75727
|
+
if (!args || !args.key) return fail$1("memory_get requires a 'key'.");
|
|
75728
|
+
const value = memory.get(scope, id, args.key);
|
|
75729
|
+
if (value === undefined)
|
|
75730
|
+
return ok$1(`No value stored for '${args.key}' (${scope}).`);
|
|
75731
|
+
return ok$1(typeof value === "string" ? value : JSON.stringify(value));
|
|
75732
|
+
}
|
|
75733
|
+
case "memory_list": {
|
|
75734
|
+
const entries = memory.list(scope, id, args && args.prefix);
|
|
75735
|
+
if (!entries.length) return ok$1(`No memory keys stored (${scope}).`);
|
|
75736
|
+
return ok$1(entries.map((e) => `${e.key} (v${e.version})`).join("\n"));
|
|
75737
|
+
}
|
|
75738
|
+
case "memory_delete": {
|
|
75739
|
+
if (!args || !args.key) return fail$1("memory_delete requires a 'key'.");
|
|
75740
|
+
const removed = memory.delete(scope, id, args.key);
|
|
75741
|
+
return ok$1(
|
|
75742
|
+
removed
|
|
75743
|
+
? `Deleted '${args.key}' (${scope}).`
|
|
75744
|
+
: `No value to delete for '${args.key}' (${scope}).`,
|
|
75745
|
+
);
|
|
75746
|
+
}
|
|
75747
|
+
default:
|
|
75748
|
+
return fail$1(`Unknown memory tool '${toolName}'.`);
|
|
75749
|
+
}
|
|
75750
|
+
} catch (e) {
|
|
75751
|
+
return fail$1(`Memory tool error: ${(e && e.message) || e}`);
|
|
75752
|
+
}
|
|
75753
|
+
}
|
|
75754
|
+
|
|
75755
|
+
var memoryTools = { MEMORY_SERVER: MEMORY_SERVER$1, MEMORY_TOOLS: MEMORY_TOOLS$1, handleMemoryTool: handleMemoryTool$1 };
|
|
75756
|
+
|
|
75757
|
+
/**
|
|
75758
|
+
* eventMatcher.js
|
|
75759
|
+
*
|
|
75760
|
+
* Given a fired event and the set of bots, returns the bots that subscribe to
|
|
75761
|
+
* it (PRD FR-009 / US-010). A bot subscribes via `subscriptions: [{ eventType }]`.
|
|
75762
|
+
*
|
|
75763
|
+
* Workspace scoping is designed in but not yet exercised: when an event carries
|
|
75764
|
+
* a `workspaceId`, a bot only matches if it's unscoped (`workspaceId` falsy) or
|
|
75765
|
+
* its `workspaceId` equals the event's. Events aren't stamped with a workspace
|
|
75766
|
+
* yet (a follow-up slice), so today every subscribed bot matches — but the
|
|
75767
|
+
* scoping "just works" once stamping lands, with no matcher change.
|
|
75768
|
+
*
|
|
75769
|
+
* Pure (NFR-006): no Electron, no I/O.
|
|
75770
|
+
*/
|
|
75771
|
+
|
|
75772
|
+
/**
|
|
75773
|
+
* @param {Array<object>} bots bot definitions (each may have `subscriptions`)
|
|
75774
|
+
* @param {{ eventType: string, workspaceId?: string }} event
|
|
75775
|
+
* @param {{ excludeBotId?: string }} [opts] loop guard — never match this bot
|
|
75776
|
+
* @returns {Array<object>} the subscribing bots
|
|
75777
|
+
*/
|
|
75778
|
+
function matchSubscribedBots$1(bots, event, opts = {}) {
|
|
75779
|
+
if (!Array.isArray(bots) || !event || !event.eventType) return [];
|
|
75780
|
+
const excludeBotId = opts.excludeBotId || null;
|
|
75781
|
+
const eventWorkspace = event.workspaceId || null;
|
|
75782
|
+
|
|
75783
|
+
return bots.filter((bot) => {
|
|
75784
|
+
if (!bot || bot.id === excludeBotId) return false;
|
|
75785
|
+
const subs = Array.isArray(bot.subscriptions) ? bot.subscriptions : [];
|
|
75786
|
+
const subscribed = subs.some((s) => s && s.eventType === event.eventType);
|
|
75787
|
+
if (!subscribed) return false;
|
|
75788
|
+
|
|
75789
|
+
// Workspace scoping (forward-compatible): only enforced once events carry a
|
|
75790
|
+
// workspaceId. An unscoped bot (no workspaceId) listens everywhere.
|
|
75791
|
+
if (eventWorkspace && bot.workspaceId) {
|
|
75792
|
+
return bot.workspaceId === eventWorkspace;
|
|
75793
|
+
}
|
|
75794
|
+
return true;
|
|
75795
|
+
});
|
|
75796
|
+
}
|
|
75797
|
+
|
|
75798
|
+
var eventMatcher = { matchSubscribedBots: matchSubscribedBots$1 };
|
|
75799
|
+
|
|
75800
|
+
/**
|
|
75801
|
+
* EventDispatcher.js
|
|
75802
|
+
*
|
|
75803
|
+
* Rate guard for event-triggered bot runs (PRD FR-009): a chatty event must not
|
|
75804
|
+
* spawn a flood of runs. Enforces a per-bot cooldown — a given bot won't be
|
|
75805
|
+
* event-triggered more often than once per `cooldownMs`. The dispatch decision
|
|
75806
|
+
* (pause, already-running) lives in botController; this owns only the timing.
|
|
75807
|
+
*
|
|
75808
|
+
* Pure (NFR-006): the clock is injected, so it's deterministic under test and
|
|
75809
|
+
* Electron-free.
|
|
75810
|
+
*/
|
|
75811
|
+
|
|
75812
|
+
const DEFAULT_COOLDOWN_MS = 10_000;
|
|
75813
|
+
|
|
75814
|
+
let EventDispatcher$1 = class EventDispatcher {
|
|
75815
|
+
/**
|
|
75816
|
+
* @param {{ cooldownMs?: number, clock?: { nowMs: () => number } }} [opts]
|
|
75817
|
+
*/
|
|
75818
|
+
constructor(opts = {}) {
|
|
75819
|
+
this._cooldownMs =
|
|
75820
|
+
typeof opts.cooldownMs === "number"
|
|
75821
|
+
? opts.cooldownMs
|
|
75822
|
+
: DEFAULT_COOLDOWN_MS;
|
|
75823
|
+
this._nowMs = (opts.clock && opts.clock.nowMs) || (() => Date.now());
|
|
75824
|
+
/** @type {Map<string, number>} botId → last dispatch time (ms) */
|
|
75825
|
+
this._last = new Map();
|
|
75826
|
+
}
|
|
75827
|
+
|
|
75828
|
+
/** @returns {boolean} true if the bot is allowed to run for an event now. */
|
|
75829
|
+
shouldDispatch(botId) {
|
|
75830
|
+
const last = this._last.get(botId);
|
|
75831
|
+
if (last === undefined) return true;
|
|
75832
|
+
return this._nowMs() - last >= this._cooldownMs;
|
|
75833
|
+
}
|
|
75834
|
+
|
|
75835
|
+
/** Record that the bot was just event-dispatched (starts its cooldown). */
|
|
75836
|
+
note(botId) {
|
|
75837
|
+
this._last.set(botId, this._nowMs());
|
|
75838
|
+
}
|
|
75839
|
+
};
|
|
75840
|
+
|
|
75841
|
+
var EventDispatcher_1 = { EventDispatcher: EventDispatcher$1, DEFAULT_COOLDOWN_MS };
|
|
75842
|
+
|
|
75494
75843
|
/**
|
|
75495
75844
|
* mcpResult.js
|
|
75496
75845
|
*
|
|
@@ -75557,6 +75906,14 @@ const BotRunner = BotRunner_1;
|
|
|
75557
75906
|
const BotScheduler = BotScheduler_1;
|
|
75558
75907
|
const BudgetController = BudgetControllerExports;
|
|
75559
75908
|
const PauseController = PauseController_1;
|
|
75909
|
+
const { BotMemory } = BotMemory_1;
|
|
75910
|
+
const {
|
|
75911
|
+
MEMORY_SERVER,
|
|
75912
|
+
MEMORY_TOOLS,
|
|
75913
|
+
handleMemoryTool,
|
|
75914
|
+
} = memoryTools;
|
|
75915
|
+
const { matchSubscribedBots } = eventMatcher;
|
|
75916
|
+
const { EventDispatcher } = EventDispatcher_1;
|
|
75560
75917
|
const { normalizeMcpResult } = mcpResult;
|
|
75561
75918
|
const {
|
|
75562
75919
|
getProvider: getProvider$1,
|
|
@@ -75578,6 +75935,25 @@ function pricingFor(providerId, model) {
|
|
|
75578
75935
|
return (m && m.pricing) || null;
|
|
75579
75936
|
}
|
|
75580
75937
|
|
|
75938
|
+
/** Build the run prompt for an event-triggered bot (P1: FR-009). */
|
|
75939
|
+
function composeEventPrompt(event) {
|
|
75940
|
+
let payload;
|
|
75941
|
+
try {
|
|
75942
|
+
payload =
|
|
75943
|
+
event.content === undefined
|
|
75944
|
+
? "(no payload)"
|
|
75945
|
+
: JSON.stringify(event.content);
|
|
75946
|
+
} catch (_e) {
|
|
75947
|
+
payload = "(unserializable payload)";
|
|
75948
|
+
}
|
|
75949
|
+
return (
|
|
75950
|
+
`An event you subscribe to just fired.\n\n` +
|
|
75951
|
+
`Event: ${event.eventType}\n` +
|
|
75952
|
+
`Payload: ${payload}\n\n` +
|
|
75953
|
+
`Follow your instructions to handle it.`
|
|
75954
|
+
);
|
|
75955
|
+
}
|
|
75956
|
+
|
|
75581
75957
|
const botController$1 = {
|
|
75582
75958
|
_ready: false,
|
|
75583
75959
|
|
|
@@ -75633,10 +76009,20 @@ const botController$1 = {
|
|
|
75633
76009
|
getPricing: pricingFor,
|
|
75634
76010
|
});
|
|
75635
76011
|
|
|
76012
|
+
// Scoped, durable bot memory (P1: FR-010), served as in-process memory_*
|
|
76013
|
+
// tools that every bot can use without a consent prompt.
|
|
76014
|
+
this._memory = new BotMemory({ persistence: host.memoryPersistence });
|
|
76015
|
+
|
|
76016
|
+
// Event-bus bridge (P1: FR-009): per-bot cooldown so a chatty event can't
|
|
76017
|
+
// flood the runner with event-triggered runs.
|
|
76018
|
+
this._dispatcher = new EventDispatcher();
|
|
76019
|
+
|
|
75636
76020
|
this._runner = new BotRunner({
|
|
75637
76021
|
engines,
|
|
75638
76022
|
store: this._store,
|
|
75639
76023
|
approvals: approvalsForRunner,
|
|
76024
|
+
// Memory tools are the bot's own sandbox — auto-allowed at the gate.
|
|
76025
|
+
internalServers: [MEMORY_SERVER],
|
|
75640
76026
|
resolveRunProfile: (bot) => this._resolveRunProfile(bot),
|
|
75641
76027
|
resolveTools: (bot) => this._resolveTools(bot),
|
|
75642
76028
|
callTool: (serverName, toolName, args, o) =>
|
|
@@ -75686,6 +76072,30 @@ const botController$1 = {
|
|
|
75686
76072
|
}
|
|
75687
76073
|
},
|
|
75688
76074
|
|
|
76075
|
+
/**
|
|
76076
|
+
* Dispatch a fired event to every subscribed bot (P1: FR-009 / US-010).
|
|
76077
|
+
* Called by dash-electron's widget-event relay tap. Guards: never re-trigger
|
|
76078
|
+
* the origin bot (loop safety), skip paused bots and bots in cooldown; a bot
|
|
76079
|
+
* already running is skipped by the runner itself.
|
|
76080
|
+
* @param {{ eventType: string, content?: any, workspaceId?: string,
|
|
76081
|
+
* originBotId?: string }} event
|
|
76082
|
+
*/
|
|
76083
|
+
handleEvent(event) {
|
|
76084
|
+
if (!this._ready || !event || !event.eventType) return;
|
|
76085
|
+
const bots = matchSubscribedBots(this._store.list(), event, {
|
|
76086
|
+
excludeBotId: event.originBotId,
|
|
76087
|
+
});
|
|
76088
|
+
for (const bot of bots) {
|
|
76089
|
+
if (this._pause.isPaused(bot.id)) continue;
|
|
76090
|
+
if (!this._dispatcher.shouldDispatch(bot.id)) continue;
|
|
76091
|
+
this._dispatcher.note(bot.id);
|
|
76092
|
+
this._run(bot.id, {
|
|
76093
|
+
prompt: composeEventPrompt(event),
|
|
76094
|
+
trigger: "event",
|
|
76095
|
+
});
|
|
76096
|
+
}
|
|
76097
|
+
},
|
|
76098
|
+
|
|
75689
76099
|
// ---- IPC-facing operations ---------------------------------------------
|
|
75690
76100
|
|
|
75691
76101
|
list() {
|
|
@@ -75869,10 +76279,24 @@ const botController$1 = {
|
|
|
75869
76279
|
toolServer[tool.name] = server.serverName;
|
|
75870
76280
|
}
|
|
75871
76281
|
}
|
|
76282
|
+
// Always-available in-process memory tools (P1: FR-010).
|
|
76283
|
+
for (const tool of MEMORY_TOOLS) {
|
|
76284
|
+
tools.push(tool);
|
|
76285
|
+
toolServer[tool.name] = MEMORY_SERVER;
|
|
76286
|
+
}
|
|
75872
76287
|
return { tools, resolveServer: (toolName) => toolServer[toolName] || null };
|
|
75873
76288
|
},
|
|
75874
76289
|
|
|
75875
76290
|
async _callTool(serverName, toolName, args, opts = {}) {
|
|
76291
|
+
// In-process memory tools bypass MCP; scoped to the calling bot's workspace.
|
|
76292
|
+
if (serverName === MEMORY_SERVER) {
|
|
76293
|
+
return handleMemoryTool(
|
|
76294
|
+
this._memory,
|
|
76295
|
+
{ workspaceId: opts.workspaceId },
|
|
76296
|
+
toolName,
|
|
76297
|
+
args,
|
|
76298
|
+
);
|
|
76299
|
+
}
|
|
75876
76300
|
const win = this._getMainWindow();
|
|
75877
76301
|
// widgetId + token are null → the widget permission gate is bypassed; bot
|
|
75878
76302
|
// tool calls are gated upstream by the bot PermissionGate.
|