@knowsuchagency/fulcrum 3.2.0 → 3.2.1
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/bin/fulcrum.js +4 -4
- package/package.json +1 -1
- package/server/index.js +95 -11
package/bin/fulcrum.js
CHANGED
|
@@ -44317,7 +44317,7 @@ var init_registry = __esm(() => {
|
|
|
44317
44317
|
},
|
|
44318
44318
|
{
|
|
44319
44319
|
name: "create_calendar_event",
|
|
44320
|
-
description: "Create a new calendar event on a CalDAV calendar",
|
|
44320
|
+
description: "Create a new calendar event on a CalDAV or Google calendar.",
|
|
44321
44321
|
category: "caldav",
|
|
44322
44322
|
keywords: ["calendar", "event", "create", "new", "schedule", "appointment", "meeting"],
|
|
44323
44323
|
defer_loading: true
|
|
@@ -46316,7 +46316,7 @@ async function runMcpServer(urlOverride, portOverride) {
|
|
|
46316
46316
|
const client = new FulcrumClient(urlOverride, portOverride);
|
|
46317
46317
|
const server = new McpServer({
|
|
46318
46318
|
name: "fulcrum",
|
|
46319
|
-
version: "3.2.
|
|
46319
|
+
version: "3.2.1"
|
|
46320
46320
|
});
|
|
46321
46321
|
registerTools(server, client);
|
|
46322
46322
|
const transport = new StdioServerTransport;
|
|
@@ -48665,7 +48665,7 @@ var marketplace_default = `{
|
|
|
48665
48665
|
"name": "fulcrum",
|
|
48666
48666
|
"source": "./",
|
|
48667
48667
|
"description": "Task orchestration for Claude Code",
|
|
48668
|
-
"version": "3.2.
|
|
48668
|
+
"version": "3.2.1",
|
|
48669
48669
|
"skills": [
|
|
48670
48670
|
"./skills/fulcrum"
|
|
48671
48671
|
],
|
|
@@ -49869,7 +49869,7 @@ function compareVersions(v1, v2) {
|
|
|
49869
49869
|
var package_default = {
|
|
49870
49870
|
name: "@knowsuchagency/fulcrum",
|
|
49871
49871
|
private: true,
|
|
49872
|
-
version: "3.2.
|
|
49872
|
+
version: "3.2.1",
|
|
49873
49873
|
description: "Harness Attention. Orchestrate Agents. Ship.",
|
|
49874
49874
|
license: "PolyForm-Perimeter-1.0.0",
|
|
49875
49875
|
type: "module",
|
package/package.json
CHANGED
package/server/index.js
CHANGED
|
@@ -1148030,6 +1148030,7 @@ ${attachment.data}`);
|
|
|
1148030
1148030
|
const resultMsg = message;
|
|
1148031
1148031
|
if (resultMsg.subtype?.startsWith("error_")) {
|
|
1148032
1148032
|
const errors2 = resultMsg.errors || ["Unknown error"];
|
|
1148033
|
+
state.claudeSessionId = undefined;
|
|
1148033
1148034
|
yield { type: "error", data: { message: errors2.join(", ") } };
|
|
1148034
1148035
|
}
|
|
1148035
1148036
|
if (resultMsg.structured_output) {
|
|
@@ -1148042,18 +1148043,21 @@ ${attachment.data}`);
|
|
|
1148042
1148043
|
});
|
|
1148043
1148044
|
}
|
|
1148044
1148045
|
}
|
|
1148045
|
-
|
|
1148046
|
-
|
|
1148047
|
-
|
|
1148048
|
-
|
|
1148049
|
-
|
|
1148050
|
-
|
|
1148051
|
-
|
|
1148052
|
-
|
|
1148046
|
+
if (currentText.trim()) {
|
|
1148047
|
+
addMessage(sessionId, {
|
|
1148048
|
+
role: "assistant",
|
|
1148049
|
+
content: currentText,
|
|
1148050
|
+
model: MODEL_MAP[effectiveModelId],
|
|
1148051
|
+
tokensIn,
|
|
1148052
|
+
tokensOut,
|
|
1148053
|
+
sessionId
|
|
1148054
|
+
});
|
|
1148055
|
+
}
|
|
1148053
1148056
|
yield { type: "done", data: {} };
|
|
1148054
1148057
|
} catch (err) {
|
|
1148055
1148058
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
1148056
1148059
|
log2.assistant.error("Assistant stream error", { sessionId, error: errorMsg });
|
|
1148060
|
+
state.claudeSessionId = undefined;
|
|
1148057
1148061
|
yield { type: "error", data: { message: errorMsg } };
|
|
1148058
1148062
|
} finally {
|
|
1148059
1148063
|
for (const tempPath of tempFiles) {
|
|
@@ -1150024,6 +1150028,38 @@ var init_opencode_channel_service = __esm(() => {
|
|
|
1150024
1150028
|
});
|
|
1150025
1150029
|
|
|
1150026
1150030
|
// server/services/channels/message-handler.ts
|
|
1150031
|
+
function recordObserverFailure() {
|
|
1150032
|
+
OBSERVER_CIRCUIT_BREAKER.failureCount++;
|
|
1150033
|
+
if (OBSERVER_CIRCUIT_BREAKER.failureCount >= OBSERVER_CIRCUIT_BREAKER.failureThreshold) {
|
|
1150034
|
+
if (OBSERVER_CIRCUIT_BREAKER.state !== "open") {
|
|
1150035
|
+
log2.messaging.warn("Observer circuit breaker OPEN \u2014 pausing observe-only processing", {
|
|
1150036
|
+
failures: OBSERVER_CIRCUIT_BREAKER.failureCount,
|
|
1150037
|
+
cooldownMs: OBSERVER_CIRCUIT_BREAKER.cooldownMs
|
|
1150038
|
+
});
|
|
1150039
|
+
}
|
|
1150040
|
+
OBSERVER_CIRCUIT_BREAKER.state = "open";
|
|
1150041
|
+
OBSERVER_CIRCUIT_BREAKER.nextProbeAt = Date.now() + OBSERVER_CIRCUIT_BREAKER.cooldownMs;
|
|
1150042
|
+
OBSERVER_CIRCUIT_BREAKER.cooldownMs = Math.min(OBSERVER_CIRCUIT_BREAKER.cooldownMs * 2, OBSERVER_CIRCUIT_BREAKER.maxCooldownMs);
|
|
1150043
|
+
}
|
|
1150044
|
+
}
|
|
1150045
|
+
function recordObserverSuccess() {
|
|
1150046
|
+
if (OBSERVER_CIRCUIT_BREAKER.state === "open") {
|
|
1150047
|
+
log2.messaging.info("Observer circuit breaker CLOSED \u2014 resuming normal processing", {
|
|
1150048
|
+
previousFailures: OBSERVER_CIRCUIT_BREAKER.failureCount
|
|
1150049
|
+
});
|
|
1150050
|
+
}
|
|
1150051
|
+
OBSERVER_CIRCUIT_BREAKER.failureCount = 0;
|
|
1150052
|
+
OBSERVER_CIRCUIT_BREAKER.state = "closed";
|
|
1150053
|
+
OBSERVER_CIRCUIT_BREAKER.nextProbeAt = 0;
|
|
1150054
|
+
OBSERVER_CIRCUIT_BREAKER.cooldownMs = 60000;
|
|
1150055
|
+
}
|
|
1150056
|
+
function isObserverCircuitOpen() {
|
|
1150057
|
+
if (OBSERVER_CIRCUIT_BREAKER.state !== "open")
|
|
1150058
|
+
return false;
|
|
1150059
|
+
if (Date.now() >= OBSERVER_CIRCUIT_BREAKER.nextProbeAt)
|
|
1150060
|
+
return false;
|
|
1150061
|
+
return true;
|
|
1150062
|
+
}
|
|
1150027
1150063
|
async function handleIncomingMessage(msg) {
|
|
1150028
1150064
|
const content = msg.content.trim();
|
|
1150029
1150065
|
const isObserveOnly = msg.metadata?.observeOnly === true;
|
|
@@ -1150271,12 +1150307,16 @@ function splitMessage(content, maxLength) {
|
|
|
1150271
1150307
|
return parts;
|
|
1150272
1150308
|
}
|
|
1150273
1150309
|
async function processObserveOnlyMessage(msg) {
|
|
1150310
|
+
if (isObserverCircuitOpen()) {
|
|
1150311
|
+
return;
|
|
1150312
|
+
}
|
|
1150274
1150313
|
const observeSessionKey = `observe-${msg.connectionId}`;
|
|
1150275
1150314
|
const { session: session3 } = getOrCreateSession(msg.connectionId, observeSessionKey, "Observer", undefined, msg.channelType);
|
|
1150276
1150315
|
const settings = getSettings();
|
|
1150277
1150316
|
const observerProvider = settings.assistant.observerProvider ?? settings.assistant.provider;
|
|
1150278
1150317
|
if (observerProvider === "opencode") {
|
|
1150279
1150318
|
try {
|
|
1150319
|
+
let hadError = false;
|
|
1150280
1150320
|
const stream2 = _deps.streamOpencodeObserverMessage(session3.id, msg.content, {
|
|
1150281
1150321
|
channelType: msg.channelType,
|
|
1150282
1150322
|
senderId: msg.senderId,
|
|
@@ -1150284,15 +1150324,21 @@ async function processObserveOnlyMessage(msg) {
|
|
|
1150284
1150324
|
});
|
|
1150285
1150325
|
for await (const event of stream2) {
|
|
1150286
1150326
|
if (event.type === "error") {
|
|
1150327
|
+
hadError = true;
|
|
1150287
1150328
|
const errorMsg = event.data.message;
|
|
1150288
1150329
|
log2.messaging.error("Error in OpenCode observe-only processing", { error: errorMsg });
|
|
1150289
1150330
|
}
|
|
1150290
1150331
|
}
|
|
1150332
|
+
if (hadError)
|
|
1150333
|
+
recordObserverFailure();
|
|
1150334
|
+
else
|
|
1150335
|
+
recordObserverSuccess();
|
|
1150291
1150336
|
} catch (err) {
|
|
1150292
1150337
|
log2.messaging.error("Error processing observe-only message via OpenCode", {
|
|
1150293
1150338
|
connectionId: msg.connectionId,
|
|
1150294
1150339
|
error: String(err)
|
|
1150295
1150340
|
});
|
|
1150341
|
+
recordObserverFailure();
|
|
1150296
1150342
|
}
|
|
1150297
1150343
|
return;
|
|
1150298
1150344
|
}
|
|
@@ -1150308,6 +1150354,7 @@ async function processObserveOnlyMessage(msg) {
|
|
|
1150308
1150354
|
};
|
|
1150309
1150355
|
const systemPrompt = getObserveOnlySystemPrompt(msg.channelType, context);
|
|
1150310
1150356
|
try {
|
|
1150357
|
+
let hadError = false;
|
|
1150311
1150358
|
const observerModelId = settings.assistant.observerModel;
|
|
1150312
1150359
|
const stream2 = _deps.streamMessage(session3.id, msg.content, {
|
|
1150313
1150360
|
systemPromptAdditions: systemPrompt,
|
|
@@ -1150316,18 +1150363,24 @@ async function processObserveOnlyMessage(msg) {
|
|
|
1150316
1150363
|
});
|
|
1150317
1150364
|
for await (const event of stream2) {
|
|
1150318
1150365
|
if (event.type === "error") {
|
|
1150366
|
+
hadError = true;
|
|
1150319
1150367
|
const errorMsg = event.data.message;
|
|
1150320
1150368
|
log2.messaging.error("Error in observe-only message processing", { error: errorMsg });
|
|
1150321
1150369
|
}
|
|
1150322
1150370
|
}
|
|
1150371
|
+
if (hadError)
|
|
1150372
|
+
recordObserverFailure();
|
|
1150373
|
+
else
|
|
1150374
|
+
recordObserverSuccess();
|
|
1150323
1150375
|
} catch (err) {
|
|
1150324
1150376
|
log2.messaging.error("Error processing observe-only message", {
|
|
1150325
1150377
|
connectionId: msg.connectionId,
|
|
1150326
1150378
|
error: String(err)
|
|
1150327
1150379
|
});
|
|
1150380
|
+
recordObserverFailure();
|
|
1150328
1150381
|
}
|
|
1150329
1150382
|
}
|
|
1150330
|
-
var _deps, SLACK_RESPONSE_SCHEMA, COMMANDS;
|
|
1150383
|
+
var _deps, SLACK_RESPONSE_SCHEMA, OBSERVER_CIRCUIT_BREAKER, COMMANDS;
|
|
1150331
1150384
|
var init_message_handler = __esm(() => {
|
|
1150332
1150385
|
init_logger3();
|
|
1150333
1150386
|
init_channel_manager();
|
|
@@ -1150354,6 +1150407,14 @@ var init_message_handler = __esm(() => {
|
|
|
1150354
1150407
|
},
|
|
1150355
1150408
|
required: ["body"]
|
|
1150356
1150409
|
};
|
|
1150410
|
+
OBSERVER_CIRCUIT_BREAKER = {
|
|
1150411
|
+
failureCount: 0,
|
|
1150412
|
+
failureThreshold: 3,
|
|
1150413
|
+
state: "closed",
|
|
1150414
|
+
nextProbeAt: 0,
|
|
1150415
|
+
cooldownMs: 60000,
|
|
1150416
|
+
maxCooldownMs: 600000
|
|
1150417
|
+
};
|
|
1150357
1150418
|
COMMANDS = {
|
|
1150358
1150419
|
RESET: ["/reset", "/new", "/clear"],
|
|
1150359
1150420
|
HELP: ["/help", "/?"],
|
|
@@ -1251751,7 +1251812,7 @@ var toolRegistry = [
|
|
|
1251751
1251812
|
},
|
|
1251752
1251813
|
{
|
|
1251753
1251814
|
name: "create_calendar_event",
|
|
1251754
|
-
description: "Create a new calendar event on a CalDAV calendar",
|
|
1251815
|
+
description: "Create a new calendar event on a CalDAV or Google calendar.",
|
|
1251755
1251816
|
category: "caldav",
|
|
1251756
1251817
|
keywords: ["calendar", "event", "create", "new", "schedule", "appointment", "meeting"],
|
|
1251757
1251818
|
defer_loading: true
|
|
@@ -1254470,7 +1254531,7 @@ mcpRoutes.all("/", async (c) => {
|
|
|
1254470
1254531
|
});
|
|
1254471
1254532
|
const server2 = new McpServer({
|
|
1254472
1254533
|
name: "fulcrum",
|
|
1254473
|
-
version: "3.2.
|
|
1254534
|
+
version: "3.2.1"
|
|
1254474
1254535
|
});
|
|
1254475
1254536
|
const client3 = new FulcrumClient(`http://localhost:${port}`);
|
|
1254476
1254537
|
registerTools(server2, client3);
|
|
@@ -1255793,6 +1255854,7 @@ init_settings();
|
|
|
1255793
1255854
|
init_logger3();
|
|
1255794
1255855
|
init_ical_helpers();
|
|
1255795
1255856
|
init_caldav_account_manager();
|
|
1255857
|
+
init_google_calendar_manager();
|
|
1255796
1255858
|
var logger9 = createLogger("CalDAV");
|
|
1255797
1255859
|
function migrateFromSettings() {
|
|
1255798
1255860
|
const settings = getSettings();
|
|
@@ -1256098,6 +1256160,20 @@ async function createEvent(input) {
|
|
|
1256098
1256160
|
if (!calendar2) {
|
|
1256099
1256161
|
throw new Error(`Calendar not found: ${input.calendarId}`);
|
|
1256100
1256162
|
}
|
|
1256163
|
+
if (calendar2.googleAccountId) {
|
|
1256164
|
+
await googleCalendarManager.createEvent(calendar2.id, {
|
|
1256165
|
+
summary: input.summary,
|
|
1256166
|
+
dtstart: input.dtstart,
|
|
1256167
|
+
dtend: input.dtend,
|
|
1256168
|
+
description: input.description,
|
|
1256169
|
+
location: input.location,
|
|
1256170
|
+
allDay: input.allDay
|
|
1256171
|
+
});
|
|
1256172
|
+
const created = db2.select().from(caldavEvents).where(eq(caldavEvents.calendarId, input.calendarId)).orderBy(desc(caldavEvents.createdAt)).limit(1).get();
|
|
1256173
|
+
if (!created)
|
|
1256174
|
+
throw new Error("Failed to retrieve created Google Calendar event");
|
|
1256175
|
+
return created;
|
|
1256176
|
+
}
|
|
1256101
1256177
|
const client4 = calendar2.accountId ? accountManager.getClient(calendar2.accountId) : null;
|
|
1256102
1256178
|
if (!client4) {
|
|
1256103
1256179
|
throw new Error("CalDAV account not connected for this calendar");
|
|
@@ -1256154,6 +1256230,10 @@ async function updateEvent(id, updates) {
|
|
|
1256154
1256230
|
throw new Error(`Event not found: ${id}`);
|
|
1256155
1256231
|
}
|
|
1256156
1256232
|
const calendar2 = db2.select().from(caldavCalendars).where(eq(caldavCalendars.id, event.calendarId)).get();
|
|
1256233
|
+
if (calendar2?.googleAccountId) {
|
|
1256234
|
+
await googleCalendarManager.updateEvent(id, updates);
|
|
1256235
|
+
return db2.select().from(caldavEvents).where(eq(caldavEvents.id, id)).get();
|
|
1256236
|
+
}
|
|
1256157
1256237
|
const client4 = calendar2?.accountId ? accountManager.getClient(calendar2.accountId) : null;
|
|
1256158
1256238
|
if (!client4) {
|
|
1256159
1256239
|
throw new Error("CalDAV account not connected for this calendar");
|
|
@@ -1256200,6 +1256280,10 @@ async function deleteEvent(id) {
|
|
|
1256200
1256280
|
throw new Error(`Event not found: ${id}`);
|
|
1256201
1256281
|
}
|
|
1256202
1256282
|
const calendar2 = db2.select().from(caldavCalendars).where(eq(caldavCalendars.id, event.calendarId)).get();
|
|
1256283
|
+
if (calendar2?.googleAccountId) {
|
|
1256284
|
+
await googleCalendarManager.deleteEvent(id);
|
|
1256285
|
+
return;
|
|
1256286
|
+
}
|
|
1256203
1256287
|
const client4 = calendar2?.accountId ? accountManager.getClient(calendar2.accountId) : null;
|
|
1256204
1256288
|
if (!client4) {
|
|
1256205
1256289
|
throw new Error("CalDAV account not connected for this calendar");
|