commonswarm 0.1.32 → 0.1.34
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/cswarm.cjs +228 -126
- package/package.json +1 -1
package/cswarm.cjs
CHANGED
|
@@ -29586,6 +29586,7 @@ var DeliveryReceiptReadError = class extends Error {
|
|
|
29586
29586
|
var ACK_OUTCOMES = /* @__PURE__ */ new Set([
|
|
29587
29587
|
"replied",
|
|
29588
29588
|
"observed",
|
|
29589
|
+
"queued",
|
|
29589
29590
|
"expired",
|
|
29590
29591
|
"failed_terminal"
|
|
29591
29592
|
]);
|
|
@@ -29795,6 +29796,7 @@ function signalReceiptCliState(receipt, nowMs) {
|
|
|
29795
29796
|
if (state === "enqueued") return "not_delivered";
|
|
29796
29797
|
if (state === "leased") return "working";
|
|
29797
29798
|
if (state === "delivered") return "delivered";
|
|
29799
|
+
if (state === "queued") return "queued";
|
|
29798
29800
|
return "finished";
|
|
29799
29801
|
}
|
|
29800
29802
|
function receiptCheckCommand(report) {
|
|
@@ -29834,6 +29836,13 @@ function renderSignalReceiptReport(report, nowMs = Date.now()) {
|
|
|
29834
29836
|
`Check for the outcome with: ${receiptCheckCommand(report)}`
|
|
29835
29837
|
].join("\n");
|
|
29836
29838
|
}
|
|
29839
|
+
if (state === "queued") {
|
|
29840
|
+
return [
|
|
29841
|
+
`Queued for agent ${receipt.recipient_agent_principal_id}'s interactive session ${relativeAge(receipt.acked_at, nowMs)}. The agent has not seen it yet.`,
|
|
29842
|
+
"It will appear at the agent's next prompt.",
|
|
29843
|
+
`Check again with: ${receiptCheckCommand(report)}`
|
|
29844
|
+
].join("\n");
|
|
29845
|
+
}
|
|
29837
29846
|
const finished = `Agent ${receipt.recipient_agent_principal_id} finished with outcome ${state} ${relativeAge(receipt.acked_at, nowMs)}.`;
|
|
29838
29847
|
if (state === "replied") {
|
|
29839
29848
|
return [
|
|
@@ -29844,7 +29853,7 @@ function renderSignalReceiptReport(report, nowMs = Date.now()) {
|
|
|
29844
29853
|
if (state === "observed") {
|
|
29845
29854
|
return [
|
|
29846
29855
|
finished,
|
|
29847
|
-
"The agent
|
|
29856
|
+
"The agent saw the signal without sending a reply.",
|
|
29848
29857
|
`If you need an answer, send a new ask with: ${newAskCommand(report, receipt)}`
|
|
29849
29858
|
].join("\n");
|
|
29850
29859
|
}
|
|
@@ -33059,7 +33068,7 @@ function parseV2Record(row) {
|
|
|
33059
33068
|
throw new Error("stored listener effect is malformed");
|
|
33060
33069
|
}
|
|
33061
33070
|
if (signalKind2 === "note") {
|
|
33062
|
-
if (typeof row.commandId !== "string" || row.commandId !== "" || row.state !== "observed" || row.promptAttempts !== 0 || row.postAttempts !== 0 || row.replyBody !== null || row.replyTruncated !== false || row.replySignalId !== null || row.failureCode !== null) {
|
|
33071
|
+
if (typeof row.commandId !== "string" || row.commandId !== "" || row.state !== "observed" && row.state !== "routed_main" || row.promptAttempts !== 0 || row.postAttempts !== 0 || row.replyBody !== null || row.replyTruncated !== false || row.replySignalId !== null || row.failureCode !== null) {
|
|
33063
33072
|
throw new Error("stored listener effect is malformed");
|
|
33064
33073
|
}
|
|
33065
33074
|
} else if (row.state === "routed_main") {
|
|
@@ -33128,11 +33137,11 @@ function newObservedNoteRecord(input) {
|
|
|
33128
33137
|
updatedAt: input.updatedAt
|
|
33129
33138
|
};
|
|
33130
33139
|
}
|
|
33131
|
-
function
|
|
33140
|
+
function newRoutedMainRecord(input) {
|
|
33132
33141
|
const base = newObservedNoteRecord(input);
|
|
33133
33142
|
return {
|
|
33134
33143
|
...base,
|
|
33135
|
-
signalKind:
|
|
33144
|
+
signalKind: input.signalKind,
|
|
33136
33145
|
state: "routed_main"
|
|
33137
33146
|
};
|
|
33138
33147
|
}
|
|
@@ -34330,6 +34339,7 @@ var SENDER_OWNER_RELATIONS2 = /* @__PURE__ */ new Set([
|
|
|
34330
34339
|
var DELIVERY_ACK_OUTCOMES = /* @__PURE__ */ new Set([
|
|
34331
34340
|
"replied",
|
|
34332
34341
|
"observed",
|
|
34342
|
+
"queued",
|
|
34333
34343
|
"expired",
|
|
34334
34344
|
"failed_terminal"
|
|
34335
34345
|
]);
|
|
@@ -34376,6 +34386,10 @@ var DELIVERY_SERVER_ERROR_CODES = Object.freeze([
|
|
|
34376
34386
|
"internal_error"
|
|
34377
34387
|
]);
|
|
34378
34388
|
var DELIVERY_UNKNOWN_ERROR_CODE = "unknown_error";
|
|
34389
|
+
function observationCommandId(signalId) {
|
|
34390
|
+
checkedUuidRequest(signalId, "signalId");
|
|
34391
|
+
return `observe_${signalId.toLowerCase().replaceAll("-", "")}`;
|
|
34392
|
+
}
|
|
34379
34393
|
var DeliveryTransportError = class extends Error {
|
|
34380
34394
|
constructor(message) {
|
|
34381
34395
|
super(message);
|
|
@@ -34672,7 +34686,7 @@ function assertAckRequest(request) {
|
|
|
34672
34686
|
checkedUuidRequest(request.listenerInstanceId, "listenerInstanceId");
|
|
34673
34687
|
if (!DELIVERY_ACK_OUTCOMES.has(request.outcome)) {
|
|
34674
34688
|
throw new Error(
|
|
34675
|
-
"a delivery outcome must be replied, observed, expired, or failed_terminal"
|
|
34689
|
+
"a delivery outcome must be replied, observed, queued, expired, or failed_terminal"
|
|
34676
34690
|
);
|
|
34677
34691
|
}
|
|
34678
34692
|
if (request.outcome === "failed_terminal") {
|
|
@@ -34864,6 +34878,34 @@ var DeliveryCommandClient = class {
|
|
|
34864
34878
|
outcome: request.outcome
|
|
34865
34879
|
};
|
|
34866
34880
|
}
|
|
34881
|
+
/** Mark a queued delivery observed only after the interactive hook surfaced it. */
|
|
34882
|
+
async observeQueuedAgentDelivery(request) {
|
|
34883
|
+
checkedCommandId(request.commandId);
|
|
34884
|
+
assertAgentToken(request.credential);
|
|
34885
|
+
checkedUuidRequest(request.workspaceId, "workspaceId");
|
|
34886
|
+
checkedUuidRequest(request.signalId, "signalId");
|
|
34887
|
+
const { response, text } = await this.post(request, {
|
|
34888
|
+
kind: "ack_agent_delivery",
|
|
34889
|
+
signal_id: request.signalId.toLowerCase(),
|
|
34890
|
+
lease_id: null,
|
|
34891
|
+
listener_instance_id: null,
|
|
34892
|
+
outcome: "observed",
|
|
34893
|
+
last_error_code: null
|
|
34894
|
+
}, "delivery observation");
|
|
34895
|
+
if (!response.ok) throw refusal(response, text);
|
|
34896
|
+
parseAckSuccess(
|
|
34897
|
+
successBody(response, text, "delivery observation"),
|
|
34898
|
+
{
|
|
34899
|
+
signalId: request.signalId.toLowerCase(),
|
|
34900
|
+
outcome: "observed"
|
|
34901
|
+
}
|
|
34902
|
+
);
|
|
34903
|
+
return {
|
|
34904
|
+
httpStatus: response.status,
|
|
34905
|
+
signalId: request.signalId.toLowerCase(),
|
|
34906
|
+
outcome: "observed"
|
|
34907
|
+
};
|
|
34908
|
+
}
|
|
34867
34909
|
};
|
|
34868
34910
|
|
|
34869
34911
|
// src/listener/main-routing.ts
|
|
@@ -34917,12 +34959,13 @@ function parseEntry(value) {
|
|
|
34917
34959
|
"senderName",
|
|
34918
34960
|
"body",
|
|
34919
34961
|
"createdAt",
|
|
34920
|
-
"queuedAt"
|
|
34962
|
+
"queuedAt",
|
|
34963
|
+
"observationPending"
|
|
34921
34964
|
]);
|
|
34922
34965
|
if (Object.keys(row).some((key2) => !allowed.has(key2))) {
|
|
34923
34966
|
throw new Error("stored pending-for-main entry is malformed");
|
|
34924
34967
|
}
|
|
34925
|
-
if (typeof row.signalId !== "string" || !UUID_RE12.test(row.signalId) || typeof row.workspaceId !== "string" || !UUID_RE12.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE12.test(row.principalId) || typeof row.fromId !== "string" || !UUID_RE12.test(row.fromId) || row.fromKind !== "user" && row.fromKind !== "agent" || !(row.kind === void 0 || row.kind === "ask" || row.kind === "note") || !(row.senderName === null || typeof row.senderName === "string" && row.senderName.length <= 200) || typeof row.body !== "string" || row.body.length < 1 || !checkedTimestamp2(row.createdAt) || !checkedTimestamp2(row.queuedAt)) {
|
|
34968
|
+
if (typeof row.signalId !== "string" || !UUID_RE12.test(row.signalId) || typeof row.workspaceId !== "string" || !UUID_RE12.test(row.workspaceId) || typeof row.principalId !== "string" || !UUID_RE12.test(row.principalId) || typeof row.fromId !== "string" || !UUID_RE12.test(row.fromId) || row.fromKind !== "user" && row.fromKind !== "agent" || !(row.kind === void 0 || row.kind === "ask" || row.kind === "note") || !(row.senderName === null || typeof row.senderName === "string" && row.senderName.length <= 200) || typeof row.body !== "string" || row.body.length < 1 || !checkedTimestamp2(row.createdAt) || !checkedTimestamp2(row.queuedAt) || !(row.observationPending === void 0 || row.observationPending === true)) {
|
|
34926
34969
|
throw new Error("stored pending-for-main entry is malformed");
|
|
34927
34970
|
}
|
|
34928
34971
|
return {
|
|
@@ -34935,7 +34978,8 @@ function parseEntry(value) {
|
|
|
34935
34978
|
senderName: row.senderName,
|
|
34936
34979
|
body: row.body,
|
|
34937
34980
|
createdAt: row.createdAt,
|
|
34938
|
-
queuedAt: row.queuedAt
|
|
34981
|
+
queuedAt: row.queuedAt,
|
|
34982
|
+
...row.observationPending === true ? { observationPending: true } : {}
|
|
34939
34983
|
};
|
|
34940
34984
|
}
|
|
34941
34985
|
function parseFile(raw) {
|
|
@@ -35031,9 +35075,9 @@ var FilePendingMainQueue = class {
|
|
|
35031
35075
|
}, lockTimeoutMs === void 0 ? {} : { timeoutMs: lockTimeoutMs });
|
|
35032
35076
|
}
|
|
35033
35077
|
};
|
|
35034
|
-
function pendingMainEntry(signal, principalId, provenance, now) {
|
|
35035
|
-
if (signal.kind !== "ask") {
|
|
35036
|
-
throw new Error("only directed asks can enter the pending-for-main queue");
|
|
35078
|
+
function pendingMainEntry(signal, principalId, provenance, now, options = {}) {
|
|
35079
|
+
if (signal.kind !== "ask" && signal.kind !== "note") {
|
|
35080
|
+
throw new Error("only directed asks and notes can enter the pending-for-main queue");
|
|
35037
35081
|
}
|
|
35038
35082
|
return parseEntry({
|
|
35039
35083
|
signalId: signal.id,
|
|
@@ -35041,10 +35085,12 @@ function pendingMainEntry(signal, principalId, provenance, now) {
|
|
|
35041
35085
|
principalId,
|
|
35042
35086
|
fromId: signal.from,
|
|
35043
35087
|
fromKind: signal.from_kind,
|
|
35088
|
+
kind: signal.kind,
|
|
35044
35089
|
senderName: provenance.senderName,
|
|
35045
35090
|
body: signal.body,
|
|
35046
35091
|
createdAt: signal.created_at,
|
|
35047
|
-
queuedAt: new Date(now).toISOString()
|
|
35092
|
+
queuedAt: new Date(now).toISOString(),
|
|
35093
|
+
...options.observationPending ? { observationPending: true } : {}
|
|
35048
35094
|
});
|
|
35049
35095
|
}
|
|
35050
35096
|
|
|
@@ -35137,8 +35183,8 @@ function ackForTerminalEffect(record, now) {
|
|
|
35137
35183
|
if (record.state === "observed" && record.signalKind === "note") {
|
|
35138
35184
|
return { outcome: "observed", lastErrorCode: null };
|
|
35139
35185
|
}
|
|
35140
|
-
if (record.state === "routed_main"
|
|
35141
|
-
return { outcome: "
|
|
35186
|
+
if (record.state === "routed_main") {
|
|
35187
|
+
return { outcome: "queued", lastErrorCode: null };
|
|
35142
35188
|
}
|
|
35143
35189
|
if (record.state === "expired" && record.signalKind === "ask" && Date.parse(record.askUntil) <= now()) {
|
|
35144
35190
|
return { outcome: "expired", lastErrorCode: null };
|
|
@@ -35159,7 +35205,7 @@ function ackForTerminalEffect(record, now) {
|
|
|
35159
35205
|
return { outcome: "failed_terminal", lastErrorCode: "local_effect_failed" };
|
|
35160
35206
|
}
|
|
35161
35207
|
function isAckableTerminalEffect(record, now) {
|
|
35162
|
-
return record.state === "done" && record.signalKind === "ask" && !!record.replySignalId || record.state === "observed" && record.signalKind === "note" || record.state === "routed_main"
|
|
35208
|
+
return record.state === "done" && record.signalKind === "ask" && !!record.replySignalId || record.state === "observed" && record.signalKind === "note" || record.state === "routed_main" || record.state === "expired" && record.signalKind === "ask" && Date.parse(record.askUntil) <= now() || record.state === "failed" && record.signalKind === "ask";
|
|
35163
35209
|
}
|
|
35164
35210
|
function effectPhaseBudget(record) {
|
|
35165
35211
|
if (record === null || record.state === "received" || record.state === "prompting") {
|
|
@@ -35170,6 +35216,9 @@ function effectPhaseBudget(record) {
|
|
|
35170
35216
|
}
|
|
35171
35217
|
return LISTENER_ACK_ONLY_MINIMUM_MS;
|
|
35172
35218
|
}
|
|
35219
|
+
function observedNoteNeedsMainRoute(record, routeMode, deferOverChars) {
|
|
35220
|
+
return record?.state === "observed" && record.signalKind === "note" && decideListenerRoute(routeMode, deferOverChars, record.askBody.length) === "main";
|
|
35221
|
+
}
|
|
35173
35222
|
function verifyPreparedAckEffect(record, active, now) {
|
|
35174
35223
|
if (record === null || record.signalId !== active.signalId || active.ack === null) {
|
|
35175
35224
|
throw new Error("prepared delivery ACK has no matching terminal effect");
|
|
@@ -35405,7 +35454,11 @@ async function runListenerRuntime(options) {
|
|
|
35405
35454
|
...options.resolveSenderProvenance === void 0 ? {} : { resolveSenderProvenance: options.resolveSenderProvenance },
|
|
35406
35455
|
isCredentialFailure: isCredentialLoss
|
|
35407
35456
|
});
|
|
35408
|
-
const
|
|
35457
|
+
const routeSignalToMain = async (signal) => {
|
|
35458
|
+
if (signal.kind !== "ask" && signal.kind !== "note") {
|
|
35459
|
+
throw new Error("only directed asks and notes can route to the main session");
|
|
35460
|
+
}
|
|
35461
|
+
const signalKind2 = signal.kind;
|
|
35409
35462
|
let provenance = {
|
|
35410
35463
|
senderName: null,
|
|
35411
35464
|
operatorId: null,
|
|
@@ -35421,7 +35474,9 @@ async function runListenerRuntime(options) {
|
|
|
35421
35474
|
}
|
|
35422
35475
|
}
|
|
35423
35476
|
const queued = await options.pendingMainQueue.enqueue(
|
|
35424
|
-
pendingMainEntry(signal, options.principalId, provenance, now()
|
|
35477
|
+
pendingMainEntry(signal, options.principalId, provenance, now(), {
|
|
35478
|
+
observationPending: deliveryMode === "durable_claim"
|
|
35479
|
+
})
|
|
35425
35480
|
);
|
|
35426
35481
|
options.onEvent?.({
|
|
35427
35482
|
type: "main_queue",
|
|
@@ -35433,13 +35488,19 @@ async function runListenerRuntime(options) {
|
|
|
35433
35488
|
});
|
|
35434
35489
|
const existing = await options.store.read(signal.id);
|
|
35435
35490
|
if (existing !== null) {
|
|
35436
|
-
if (!sameEffectSignal(existing, signal)
|
|
35437
|
-
throw new Error("stored listener effect does not match the main-routed
|
|
35491
|
+
if (!sameEffectSignal(existing, signal)) {
|
|
35492
|
+
throw new Error("stored listener effect does not match the main-routed message");
|
|
35493
|
+
}
|
|
35494
|
+
if (existing.state === "routed_main") {
|
|
35495
|
+
return existing;
|
|
35496
|
+
}
|
|
35497
|
+
if (!(existing.signalKind === "note" && existing.state === "observed")) {
|
|
35498
|
+
throw new Error("stored listener effect does not match the main-routed message");
|
|
35438
35499
|
}
|
|
35439
|
-
return existing;
|
|
35440
35500
|
}
|
|
35441
|
-
await options.store.write(
|
|
35501
|
+
await options.store.write(newRoutedMainRecord({
|
|
35442
35502
|
signalId: signal.id,
|
|
35503
|
+
signalKind: signalKind2,
|
|
35443
35504
|
body: signal.body,
|
|
35444
35505
|
until: signal.until,
|
|
35445
35506
|
senderOwnerRelation: signal.sender_owner_relation ?? "unknown",
|
|
@@ -35447,7 +35508,7 @@ async function runListenerRuntime(options) {
|
|
|
35447
35508
|
}));
|
|
35448
35509
|
const persisted = await options.store.read(signal.id);
|
|
35449
35510
|
if (persisted === null || !sameEffectSignal(persisted, signal) || persisted.state !== "routed_main") {
|
|
35450
|
-
throw new Error("main-routed
|
|
35511
|
+
throw new Error("main-routed message effect could not be verified");
|
|
35451
35512
|
}
|
|
35452
35513
|
return persisted;
|
|
35453
35514
|
};
|
|
@@ -35669,7 +35730,20 @@ async function runListenerRuntime(options) {
|
|
|
35669
35730
|
const recovery = currentJournalRecord?.active ?? null;
|
|
35670
35731
|
if (recovery?.phase === "ack_pending") {
|
|
35671
35732
|
const horizon = Date.parse(recovery.leasedUntil) + LISTENER_DELIVERY_SAFETY_MARGIN_MS;
|
|
35672
|
-
|
|
35733
|
+
let preparedNeedsMainRoute = false;
|
|
35734
|
+
if (recovery.signalId !== null) {
|
|
35735
|
+
try {
|
|
35736
|
+
preparedNeedsMainRoute = observedNoteNeedsMainRoute(
|
|
35737
|
+
await options.store.read(recovery.signalId),
|
|
35738
|
+
routeMode,
|
|
35739
|
+
deferOverChars
|
|
35740
|
+
);
|
|
35741
|
+
} catch (error) {
|
|
35742
|
+
stop = { reason: "fatal", error: asError2(error) };
|
|
35743
|
+
break;
|
|
35744
|
+
}
|
|
35745
|
+
}
|
|
35746
|
+
if (page.capabilities.deliveryAck && now() < horizon && !preparedNeedsMainRoute) {
|
|
35673
35747
|
const ackStop = await sendPreparedAck(recovery);
|
|
35674
35748
|
if (ackStop !== null) {
|
|
35675
35749
|
stop = ackStop;
|
|
@@ -35708,7 +35782,7 @@ async function runListenerRuntime(options) {
|
|
|
35708
35782
|
terminal = null;
|
|
35709
35783
|
}
|
|
35710
35784
|
}
|
|
35711
|
-
if (terminal !== null && sameRecoveredEffect(recovery, terminal) && isAckableTerminalEffect(terminal, now)) {
|
|
35785
|
+
if (terminal !== null && sameRecoveredEffect(recovery, terminal) && !observedNoteNeedsMainRoute(terminal, routeMode, deferOverChars) && isAckableTerminalEffect(terminal, now)) {
|
|
35712
35786
|
try {
|
|
35713
35787
|
const mapped = ackForTerminalEffect(terminal, now);
|
|
35714
35788
|
await options.deliveryJournal.prepareAck({
|
|
@@ -35902,7 +35976,33 @@ async function runListenerRuntime(options) {
|
|
|
35902
35976
|
if (existing !== null && !sameEffectSignal(existing, signal)) {
|
|
35903
35977
|
throw new Error("stored listener effect does not match the authoritative delivery");
|
|
35904
35978
|
}
|
|
35905
|
-
if (signal.kind
|
|
35979
|
+
if (signal.kind !== "ask" && signal.kind !== "note") {
|
|
35980
|
+
throw new Error("claimed delivery has an unsupported signal kind");
|
|
35981
|
+
}
|
|
35982
|
+
const decision = decideListenerRoute(
|
|
35983
|
+
routeMode,
|
|
35984
|
+
deferOverChars,
|
|
35985
|
+
signal.body.length
|
|
35986
|
+
);
|
|
35987
|
+
options.onEvent?.({
|
|
35988
|
+
type: "routing_decision",
|
|
35989
|
+
signalId: signal.id,
|
|
35990
|
+
routeMode,
|
|
35991
|
+
decision,
|
|
35992
|
+
threshold: deferOverChars,
|
|
35993
|
+
bodyLength: signal.body.length,
|
|
35994
|
+
ts: eventTime(now)
|
|
35995
|
+
});
|
|
35996
|
+
if (decision === "main") {
|
|
35997
|
+
terminal = await routeSignalToMain(signal);
|
|
35998
|
+
options.onEvent?.({
|
|
35999
|
+
type: "effect",
|
|
36000
|
+
signalId: signal.id,
|
|
36001
|
+
status: "routed_main",
|
|
36002
|
+
failureCode: null,
|
|
36003
|
+
ts: eventTime(now)
|
|
36004
|
+
});
|
|
36005
|
+
} else if (signal.kind === "note") {
|
|
35906
36006
|
if (existing === null) {
|
|
35907
36007
|
await options.store.write(newObservedNoteRecord({
|
|
35908
36008
|
signalId: signal.id,
|
|
@@ -35923,85 +36023,58 @@ async function runListenerRuntime(options) {
|
|
|
35923
36023
|
failureCode: null,
|
|
35924
36024
|
ts: eventTime(now)
|
|
35925
36025
|
});
|
|
35926
|
-
} else
|
|
35927
|
-
|
|
35928
|
-
|
|
35929
|
-
|
|
35930
|
-
signal
|
|
35931
|
-
|
|
35932
|
-
|
|
35933
|
-
|
|
35934
|
-
|
|
35935
|
-
|
|
35936
|
-
|
|
35937
|
-
|
|
35938
|
-
|
|
35939
|
-
|
|
35940
|
-
|
|
35941
|
-
|
|
35942
|
-
|
|
36026
|
+
} else {
|
|
36027
|
+
let processAttempt = 0;
|
|
36028
|
+
while (terminal === null) {
|
|
36029
|
+
const before = await options.store.read(signal.id);
|
|
36030
|
+
if (before !== null && !sameEffectSignal(before, signal)) {
|
|
36031
|
+
throw new Error("stored listener effect does not match the authoritative delivery");
|
|
36032
|
+
}
|
|
36033
|
+
const requiredBudget = effectPhaseBudget(before);
|
|
36034
|
+
if (leasedUntilMs <= now() + requiredBudget) {
|
|
36035
|
+
await sleep2(
|
|
36036
|
+
Math.max(
|
|
36037
|
+
0,
|
|
36038
|
+
leasedUntilMs + LISTENER_DELIVERY_SAFETY_MARGIN_MS - now()
|
|
36039
|
+
),
|
|
36040
|
+
abort
|
|
36041
|
+
);
|
|
36042
|
+
if (abort?.aborted) {
|
|
36043
|
+
stop = { reason: "cancelled" };
|
|
36044
|
+
break;
|
|
36045
|
+
}
|
|
36046
|
+
if (now() >= leasedUntilMs + LISTENER_DELIVERY_SAFETY_MARGIN_MS) {
|
|
36047
|
+
await journal.clearActive(eventTime(now));
|
|
36048
|
+
after = null;
|
|
36049
|
+
}
|
|
36050
|
+
break;
|
|
36051
|
+
}
|
|
36052
|
+
const processed = await engine.process(signal);
|
|
36053
|
+
const effect = "record" in processed ? processed.record : null;
|
|
35943
36054
|
options.onEvent?.({
|
|
35944
36055
|
type: "effect",
|
|
35945
36056
|
signalId: signal.id,
|
|
35946
|
-
status:
|
|
35947
|
-
failureCode: null,
|
|
36057
|
+
status: processed.status,
|
|
36058
|
+
failureCode: effect?.failureCode ?? null,
|
|
35948
36059
|
ts: eventTime(now)
|
|
35949
36060
|
});
|
|
35950
|
-
|
|
35951
|
-
|
|
35952
|
-
|
|
35953
|
-
|
|
35954
|
-
|
|
35955
|
-
|
|
35956
|
-
|
|
35957
|
-
|
|
35958
|
-
|
|
35959
|
-
|
|
35960
|
-
|
|
35961
|
-
0,
|
|
35962
|
-
leasedUntilMs + LISTENER_DELIVERY_SAFETY_MARGIN_MS - now()
|
|
35963
|
-
),
|
|
35964
|
-
abort
|
|
35965
|
-
);
|
|
35966
|
-
if (abort?.aborted) {
|
|
35967
|
-
stop = { reason: "cancelled" };
|
|
35968
|
-
break;
|
|
35969
|
-
}
|
|
35970
|
-
if (now() >= leasedUntilMs + LISTENER_DELIVERY_SAFETY_MARGIN_MS) {
|
|
35971
|
-
await journal.clearActive(eventTime(now));
|
|
35972
|
-
after = null;
|
|
35973
|
-
}
|
|
36061
|
+
if (processed.status === "ignored") {
|
|
36062
|
+
throw new Error("claimed delivery was ignored by the listener engine");
|
|
36063
|
+
}
|
|
36064
|
+
if (processed.status === "retry_pending") {
|
|
36065
|
+
processAttempt += 1;
|
|
36066
|
+
await sleep2(
|
|
36067
|
+
deliveryRetryDelay(processAttempt, null, random),
|
|
36068
|
+
abort
|
|
36069
|
+
);
|
|
36070
|
+
if (abort?.aborted) {
|
|
36071
|
+
stop = { reason: "cancelled" };
|
|
35974
36072
|
break;
|
|
35975
36073
|
}
|
|
35976
|
-
|
|
35977
|
-
const effect = "record" in processed ? processed.record : null;
|
|
35978
|
-
options.onEvent?.({
|
|
35979
|
-
type: "effect",
|
|
35980
|
-
signalId: signal.id,
|
|
35981
|
-
status: processed.status,
|
|
35982
|
-
failureCode: effect?.failureCode ?? null,
|
|
35983
|
-
ts: eventTime(now)
|
|
35984
|
-
});
|
|
35985
|
-
if (processed.status === "ignored") {
|
|
35986
|
-
throw new Error("claimed delivery was ignored by the listener engine");
|
|
35987
|
-
}
|
|
35988
|
-
if (processed.status === "retry_pending") {
|
|
35989
|
-
processAttempt += 1;
|
|
35990
|
-
await sleep2(
|
|
35991
|
-
deliveryRetryDelay(processAttempt, null, random),
|
|
35992
|
-
abort
|
|
35993
|
-
);
|
|
35994
|
-
if (abort?.aborted) {
|
|
35995
|
-
stop = { reason: "cancelled" };
|
|
35996
|
-
break;
|
|
35997
|
-
}
|
|
35998
|
-
continue;
|
|
35999
|
-
}
|
|
36000
|
-
terminal = processed.record;
|
|
36074
|
+
continue;
|
|
36001
36075
|
}
|
|
36076
|
+
terminal = processed.record;
|
|
36002
36077
|
}
|
|
36003
|
-
} else {
|
|
36004
|
-
throw new Error("claimed delivery has an unsupported signal kind");
|
|
36005
36078
|
}
|
|
36006
36079
|
} catch (error) {
|
|
36007
36080
|
if (abort?.aborted) {
|
|
@@ -36050,24 +36123,7 @@ async function runListenerRuntime(options) {
|
|
|
36050
36123
|
stop = { reason: "cancelled" };
|
|
36051
36124
|
break;
|
|
36052
36125
|
}
|
|
36053
|
-
if (signal.kind
|
|
36054
|
-
try {
|
|
36055
|
-
await readOrReplaceUnreadableEffect(options.store, signal, now);
|
|
36056
|
-
const record2 = await observeFallbackNote(options.store, signal, now);
|
|
36057
|
-
options.onEvent?.({
|
|
36058
|
-
type: "effect",
|
|
36059
|
-
signalId: signal.id,
|
|
36060
|
-
status: "observed",
|
|
36061
|
-
failureCode: record2.failureCode,
|
|
36062
|
-
ts: eventTime(now)
|
|
36063
|
-
});
|
|
36064
|
-
} catch (error) {
|
|
36065
|
-
stop = { reason: "fatal", error: asError2(error) };
|
|
36066
|
-
break;
|
|
36067
|
-
}
|
|
36068
|
-
continue;
|
|
36069
|
-
}
|
|
36070
|
-
if (signal.kind !== "ask") continue;
|
|
36126
|
+
if (signal.kind !== "ask" && signal.kind !== "note") continue;
|
|
36071
36127
|
let result;
|
|
36072
36128
|
try {
|
|
36073
36129
|
await readOrReplaceUnreadableEffect(options.store, signal, now);
|
|
@@ -36086,7 +36142,7 @@ async function runListenerRuntime(options) {
|
|
|
36086
36142
|
ts: eventTime(now)
|
|
36087
36143
|
});
|
|
36088
36144
|
if (decision === "main") {
|
|
36089
|
-
|
|
36145
|
+
await routeSignalToMain(signal);
|
|
36090
36146
|
options.onEvent?.({
|
|
36091
36147
|
type: "effect",
|
|
36092
36148
|
signalId: signal.id,
|
|
@@ -36096,6 +36152,17 @@ async function runListenerRuntime(options) {
|
|
|
36096
36152
|
});
|
|
36097
36153
|
continue;
|
|
36098
36154
|
}
|
|
36155
|
+
if (signal.kind === "note") {
|
|
36156
|
+
const record2 = await observeFallbackNote(options.store, signal, now);
|
|
36157
|
+
options.onEvent?.({
|
|
36158
|
+
type: "effect",
|
|
36159
|
+
signalId: signal.id,
|
|
36160
|
+
status: "observed",
|
|
36161
|
+
failureCode: record2.failureCode,
|
|
36162
|
+
ts: eventTime(now)
|
|
36163
|
+
});
|
|
36164
|
+
continue;
|
|
36165
|
+
}
|
|
36099
36166
|
result = await engine.process(signal);
|
|
36100
36167
|
} catch (error) {
|
|
36101
36168
|
if (abort?.aborted) {
|
|
@@ -36341,6 +36408,7 @@ async function appendListenerEvent(paths, event) {
|
|
|
36341
36408
|
const deliveryOutcomes = /* @__PURE__ */ new Set([
|
|
36342
36409
|
"replied",
|
|
36343
36410
|
"observed",
|
|
36411
|
+
"queued",
|
|
36344
36412
|
"expired",
|
|
36345
36413
|
"failed_terminal"
|
|
36346
36414
|
]);
|
|
@@ -37185,6 +37253,7 @@ var ALLOWED_PHASES = /* @__PURE__ */ new Set(["claim_pending", "leased", "ack_pe
|
|
|
37185
37253
|
var ALLOWED_OUTCOMES = /* @__PURE__ */ new Set([
|
|
37186
37254
|
"replied",
|
|
37187
37255
|
"observed",
|
|
37256
|
+
"queued",
|
|
37188
37257
|
"expired",
|
|
37189
37258
|
"failed_terminal"
|
|
37190
37259
|
]);
|
|
@@ -38310,6 +38379,32 @@ function renderDroppedAsks(count2) {
|
|
|
38310
38379
|
}
|
|
38311
38380
|
var CREDENTIAL_READ_WARNING = "CommonSwarm could not read the configured listener credential safely. The hook is not checking routed asks. Restart the listener with a fresh credential; any credential state file must be mode 0600. Then run cswarm listen status.";
|
|
38312
38381
|
var CREDENTIAL_401_WARNING = "CommonSwarm could not authenticate a listener credential (HTTP 401). The hook is not checking routed asks. Restart the listener with a fresh credential, then run cswarm listen status.";
|
|
38382
|
+
async function recordQueuedObservations(check, signalIds, options, now) {
|
|
38383
|
+
const stored = check.context.credential;
|
|
38384
|
+
const remainingMs = options.deadlineMs - now();
|
|
38385
|
+
if (signalIds.length === 0 || stored === null || options.signal.aborted || remainingMs <= 0) {
|
|
38386
|
+
return /* @__PURE__ */ new Set();
|
|
38387
|
+
}
|
|
38388
|
+
const client = new DeliveryCommandClient(
|
|
38389
|
+
cloudTarget(stored.targetUrl, stored.anonKey),
|
|
38390
|
+
options.fetcher ?? fetch,
|
|
38391
|
+
{ deadlineMs: remainingMs, now }
|
|
38392
|
+
);
|
|
38393
|
+
const results = await Promise.all(signalIds.map(async (signalId) => {
|
|
38394
|
+
try {
|
|
38395
|
+
await client.observeQueuedAgentDelivery({
|
|
38396
|
+
workspaceId: stored.workspaceId,
|
|
38397
|
+
credential: stored.credential,
|
|
38398
|
+
commandId: observationCommandId(signalId),
|
|
38399
|
+
signalId
|
|
38400
|
+
});
|
|
38401
|
+
return signalId;
|
|
38402
|
+
} catch {
|
|
38403
|
+
return null;
|
|
38404
|
+
}
|
|
38405
|
+
}));
|
|
38406
|
+
return new Set(results.filter((signalId) => signalId !== null));
|
|
38407
|
+
}
|
|
38313
38408
|
async function checkListenerHooks(options) {
|
|
38314
38409
|
try {
|
|
38315
38410
|
const stateDirectory2 = options.stateDirectory ?? defaultListenerStateDirectory();
|
|
@@ -38388,10 +38483,8 @@ async function checkListenerHooks(options) {
|
|
|
38388
38483
|
check,
|
|
38389
38484
|
store: store2,
|
|
38390
38485
|
signalIds: staged.unseen.map((item) => item.signalId),
|
|
38391
|
-
|
|
38392
|
-
|
|
38393
|
-
// without re-printing entries below the exactly-once high-water.
|
|
38394
|
-
settledPendingSignalIds: check.pending.map((item) => item.signalId),
|
|
38486
|
+
plainPendingSignalIds: check.pending.filter((item) => item.observationPending !== true).map((item) => item.signalId),
|
|
38487
|
+
observationPendingSignalIds: check.pending.filter((item) => item.observationPending === true).map((item) => item.signalId),
|
|
38395
38488
|
reportDrops,
|
|
38396
38489
|
reportCredentialFailure
|
|
38397
38490
|
});
|
|
@@ -38404,8 +38497,17 @@ async function checkListenerHooks(options) {
|
|
|
38404
38497
|
...commit.reportDrops ? { droppedCount: commit.check.droppedCount } : {},
|
|
38405
38498
|
...commit.reportCredentialFailure ? { credentialFailureReported: true } : commit.check.credentialHealthy ? { credentialFailureReported: false } : {}
|
|
38406
38499
|
});
|
|
38500
|
+
const observedSignalIds = await recordQueuedObservations(
|
|
38501
|
+
commit.check,
|
|
38502
|
+
commit.observationPendingSignalIds,
|
|
38503
|
+
options,
|
|
38504
|
+
now
|
|
38505
|
+
);
|
|
38407
38506
|
const remainingCount = await commit.check.queue.remove(
|
|
38408
|
-
new Set(
|
|
38507
|
+
/* @__PURE__ */ new Set([
|
|
38508
|
+
...commit.plainPendingSignalIds,
|
|
38509
|
+
...observedSignalIds
|
|
38510
|
+
]),
|
|
38409
38511
|
HOOK_LOCK_TIMEOUT_MS
|
|
38410
38512
|
);
|
|
38411
38513
|
if (commit.check.context.paths !== null && commit.check.context.status !== null) {
|
|
@@ -38538,8 +38640,8 @@ var ACCEPTED_AGENT_CREDENTIAL_MESSAGES = [
|
|
|
38538
38640
|
AGENT_CREDENTIAL_MESSAGE_D088
|
|
38539
38641
|
];
|
|
38540
38642
|
function packageVersion() {
|
|
38541
|
-
if ("0.1.
|
|
38542
|
-
return "0.1.
|
|
38643
|
+
if ("0.1.34".length > 0) {
|
|
38644
|
+
return "0.1.34";
|
|
38543
38645
|
}
|
|
38544
38646
|
try {
|
|
38545
38647
|
const value = JSON.parse(
|
|
@@ -38738,11 +38840,11 @@ TTL); a turn that lands just before a rotation can be clamped to the ~5m
|
|
|
38738
38840
|
renewal lead, and if it times out there, durable delivery retries it on the
|
|
38739
38841
|
fresh credential.
|
|
38740
38842
|
|
|
38741
|
-
listen start --route worker|main|split chooses where directed
|
|
38742
|
-
is the unchanged default. main queues every ask for the interactive session.
|
|
38743
|
-
split queues
|
|
38744
|
-
1..10000 and an equal-length
|
|
38745
|
-
to surface queued
|
|
38843
|
+
listen start --route worker|main|split chooses where directed messages go. worker
|
|
38844
|
+
is the unchanged default. main queues every ask or note for the interactive session.
|
|
38845
|
+
split queues messages whose body is longer than --defer-over <chars>; the bound is
|
|
38846
|
+
1..10000 and an equal-length message stays on the worker path. Run cswarm hook check
|
|
38847
|
+
to surface queued messages. hook check has its own 3s ceiling, exits 0 on every
|
|
38746
38848
|
outcome, and skips network checks made within --cooldown seconds (default 30).
|
|
38747
38849
|
hook install claude prints the UserPromptSubmit JSON by default and changes the
|
|
38748
38850
|
project's .claude/settings.json only with --write; uninstall also requires --write.
|