@ycodium-ai/plugin-im 0.2.3214 → 0.2.3220
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.js +449 -216
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -126258,67 +126258,96 @@ var keepNewest = (record4, limit) => {
|
|
|
126258
126258
|
return entries.length <= limit ? record4 : Object.fromEntries(entries.slice(entries.length - limit));
|
|
126259
126259
|
};
|
|
126260
126260
|
|
|
126261
|
-
// src/
|
|
126262
|
-
var
|
|
126263
|
-
const
|
|
126264
|
-
|
|
126265
|
-
|
|
126266
|
-
|
|
126267
|
-
|
|
126268
|
-
|
|
126269
|
-
|
|
126270
|
-
|
|
126271
|
-
if (oldest !== void 0) entries.delete(oldest);
|
|
126272
|
-
}
|
|
126273
|
-
},
|
|
126274
|
-
/** 忘掉键满足条件的那些。 */
|
|
126275
|
-
forget: (matches) => {
|
|
126276
|
-
for (const key of [...entries.keys()]) if (matches(key)) entries.delete(key);
|
|
126261
|
+
// src/executionTurn.ts
|
|
126262
|
+
var indexExecution = (execution) => {
|
|
126263
|
+
const intentsByMessageId = /* @__PURE__ */ new Map();
|
|
126264
|
+
const intentsById = /* @__PURE__ */ new Map();
|
|
126265
|
+
const turnsById = /* @__PURE__ */ new Map();
|
|
126266
|
+
for (const intent of execution.turnIntents) {
|
|
126267
|
+
if (!intentsByMessageId.has(intent.messageId)) intentsByMessageId.set(intent.messageId, intent);
|
|
126268
|
+
if (!intentsById.has(intent.intentId)) intentsById.set(intent.intentId, intent);
|
|
126269
|
+
if (intent.turn?.turnId != null && !turnsById.has(intent.turn.turnId)) {
|
|
126270
|
+
turnsById.set(intent.turn.turnId, intent.turn);
|
|
126277
126271
|
}
|
|
126278
|
-
}
|
|
126272
|
+
}
|
|
126273
|
+
if (execution.latestTurn != null && !turnsById.has(execution.latestTurn.turnId)) {
|
|
126274
|
+
turnsById.set(execution.latestTurn.turnId, execution.latestTurn);
|
|
126275
|
+
}
|
|
126276
|
+
return { execution, intentsByMessageId, intentsById, turnsById };
|
|
126279
126277
|
};
|
|
126278
|
+
var executionTurnFor = (snapshot, turnId) => snapshot.turnsById.get(turnId);
|
|
126280
126279
|
|
|
126281
|
-
// src/
|
|
126282
|
-
var
|
|
126283
|
-
|
|
126284
|
-
|
|
126285
|
-
var
|
|
126286
|
-
|
|
126287
|
-
const
|
|
126288
|
-
|
|
126280
|
+
// src/intentStatus.ts
|
|
126281
|
+
var isLiveIntentStatus = (status) => status === "queued" || status === "preparing" || status === "dispatching" || status === "running" || status === "reconciling" || status === "blocked";
|
|
126282
|
+
|
|
126283
|
+
// src/conclusionTurn.ts
|
|
126284
|
+
var conclusionOf = (turn) => turn.state === "completed" || turn.state === "interrupted" || turn.state === "error" ? { kind: "settled", ...turn, state: turn.state } : { kind: "pending" };
|
|
126285
|
+
var conclusionTurnFor = (snapshot, userMessageId) => {
|
|
126286
|
+
const { execution } = snapshot;
|
|
126287
|
+
const initial = snapshot.intentsByMessageId.get(userMessageId);
|
|
126288
|
+
if (initial === void 0) {
|
|
126289
|
+
const latest = execution.latestTurn;
|
|
126290
|
+
return latest?.userMessageId === userMessageId ? conclusionOf(latest) : { kind: "gone" };
|
|
126291
|
+
}
|
|
126292
|
+
let intent = initial;
|
|
126293
|
+
const visited = /* @__PURE__ */ new Set();
|
|
126294
|
+
while (intent.status === "cancelled" || intent.status === "abandoned") {
|
|
126295
|
+
if (visited.has(intent.intentId)) return { kind: "gone" };
|
|
126296
|
+
visited.add(intent.intentId);
|
|
126297
|
+
const replacementId = intent.replacementIntentId;
|
|
126298
|
+
const replacement = replacementId == null ? void 0 : snapshot.intentsById.get(replacementId);
|
|
126299
|
+
if (replacement === void 0) return { kind: "gone" };
|
|
126300
|
+
intent = replacement;
|
|
126301
|
+
}
|
|
126302
|
+
const action = intent.action;
|
|
126303
|
+
const turn = intent.turn;
|
|
126304
|
+
if (action?.kind === "steer" && intent.deliveredTurnId === action.turnId) {
|
|
126305
|
+
return {
|
|
126306
|
+
kind: "merged",
|
|
126307
|
+
turnId: action.turnId,
|
|
126308
|
+
turn: executionTurnFor(snapshot, action.turnId)
|
|
126309
|
+
};
|
|
126310
|
+
}
|
|
126311
|
+
if (turn !== null) return conclusionOf(turn);
|
|
126312
|
+
if (action?.kind === "steer") return { kind: "pending" };
|
|
126313
|
+
return { kind: isLiveIntentStatus(intent.status) ? "pending" : "gone" };
|
|
126289
126314
|
};
|
|
126290
|
-
var
|
|
126291
|
-
const
|
|
126292
|
-
|
|
126293
|
-
|
|
126294
|
-
|
|
126295
|
-
|
|
126296
|
-
|
|
126297
|
-
|
|
126315
|
+
var groupConclusionDebts = (snapshot, threadId, debts) => {
|
|
126316
|
+
const resolved = new Map(
|
|
126317
|
+
debts.filter((debt) => debt.threadId === threadId).map((debt) => [
|
|
126318
|
+
debt.userMessageId,
|
|
126319
|
+
{ debt, result: conclusionTurnFor(snapshot, debt.userMessageId) }
|
|
126320
|
+
])
|
|
126321
|
+
);
|
|
126322
|
+
const byTurn = /* @__PURE__ */ new Map();
|
|
126323
|
+
for (const { debt, result: result2 } of resolved.values()) {
|
|
126324
|
+
if (result2.kind !== "settled" && result2.kind !== "merged") continue;
|
|
126325
|
+
const group = byTurn.get(result2.turnId) ?? [];
|
|
126326
|
+
group.push(debt);
|
|
126327
|
+
byTurn.set(result2.turnId, group);
|
|
126328
|
+
}
|
|
126329
|
+
for (const [turnId, candidates] of byTurn) {
|
|
126330
|
+
const ownerId = turnId === null ? void 0 : executionTurnFor(snapshot, turnId)?.userMessageId;
|
|
126331
|
+
const owner = ownerId === void 0 ? void 0 : resolved.get(ownerId);
|
|
126332
|
+
if (owner !== void 0 && owner.result.kind !== "gone" && !candidates.includes(owner.debt))
|
|
126333
|
+
candidates.push(owner.debt);
|
|
126334
|
+
}
|
|
126335
|
+
return { resolved, byTurn };
|
|
126336
|
+
};
|
|
126337
|
+
var conclusionDebtForTurn = (snapshot, turnId, candidates, isDelivered) => {
|
|
126338
|
+
const turn = turnId === null ? void 0 : executionTurnFor(snapshot, turnId);
|
|
126339
|
+
const own = candidates.find((debt2) => debt2.userMessageId === turn?.userMessageId);
|
|
126340
|
+
const handled = turn?.userMessageId !== void 0 && isDelivered(turn.userMessageId) || candidates.length === 0 && snapshot.execution.turnIntents.some((intent) => {
|
|
126341
|
+
if (!isDelivered(intent.messageId)) return false;
|
|
126342
|
+
const result2 = conclusionTurnFor(snapshot, intent.messageId);
|
|
126343
|
+
return (result2.kind === "merged" || result2.kind === "settled") && result2.turnId === turnId;
|
|
126344
|
+
});
|
|
126345
|
+
const debt = handled ? void 0 : own ?? candidates[0];
|
|
126298
126346
|
return {
|
|
126299
|
-
|
|
126300
|
-
|
|
126301
|
-
|
|
126302
|
-
|
|
126303
|
-
note: (threadId, userMessageId, binding) => {
|
|
126304
|
-
if (binding.reply === void 0) return;
|
|
126305
|
-
const turns = byThread.get(threadId) ?? makeBoundedMap(TURNS_PER_THREAD);
|
|
126306
|
-
byThread.set(threadId, turns);
|
|
126307
|
-
turns.set(userMessageId, { chatKey: chatKeyOf(binding), anchor: binding.reply });
|
|
126308
|
-
},
|
|
126309
|
-
/**
|
|
126310
|
-
* 给发往这条会话的聊天挂上 `turnId` 那一轮的锚点(null 的含义见上);取不到就原样返回。
|
|
126311
|
-
*
|
|
126312
|
-
* 锚点只配给它那条消息所在的聊天:会话可以被另一个聊天接走(`/resume`),而回复接口只按被
|
|
126313
|
-
* 回复的消息寻址,挂错了答复就发回原来的聊天。
|
|
126314
|
-
*/
|
|
126315
|
-
anchored: async (binding, threadId, turnId) => {
|
|
126316
|
-
const turns = byThread.get(threadId);
|
|
126317
|
-
if (turns === void 0) return binding;
|
|
126318
|
-
const asked = await userMessageOfTurn(threadId, turnId);
|
|
126319
|
-
const noted = asked === void 0 ? void 0 : turns.get(asked);
|
|
126320
|
-
return noted?.chatKey === chatKeyOf(binding) ? { ...binding, reply: noted.anchor } : binding;
|
|
126321
|
-
}
|
|
126347
|
+
debt,
|
|
126348
|
+
handled,
|
|
126349
|
+
userMessageId: turn?.userMessageId,
|
|
126350
|
+
redundant: candidates.filter((candidate) => candidate !== debt)
|
|
126322
126351
|
};
|
|
126323
126352
|
};
|
|
126324
126353
|
|
|
@@ -126336,8 +126365,19 @@ var foldConclusionEvent = (state, name, data) => {
|
|
|
126336
126365
|
const pending = conclusionDebtsOf(state);
|
|
126337
126366
|
if (name === "conclusion-delivered") {
|
|
126338
126367
|
const { [key]: _delivered, ...rest } = pending;
|
|
126339
|
-
|
|
126368
|
+
const turnUserMessageId = data["turnUserMessageId"];
|
|
126369
|
+
const turnKey2 = typeof turnUserMessageId === "string" && turnUserMessageId !== "" ? keyOf(threadId, turnUserMessageId) : key;
|
|
126370
|
+
const { [turnKey2]: _turnDelivered, ...remaining } = rest;
|
|
126371
|
+
return {
|
|
126372
|
+
...state,
|
|
126373
|
+
conclusions: remaining,
|
|
126374
|
+
deliveredConclusions: keepNewest(
|
|
126375
|
+
{ ...state.deliveredConclusions, [key]: true, [turnKey2]: true },
|
|
126376
|
+
CONCLUSION_DEBT_LIMIT
|
|
126377
|
+
)
|
|
126378
|
+
};
|
|
126340
126379
|
}
|
|
126380
|
+
if (state.deliveredConclusions?.[key] === true) return state;
|
|
126341
126381
|
const chatKey = data["chatKey"];
|
|
126342
126382
|
const recordedAt = data["recordedAt"];
|
|
126343
126383
|
if (typeof chatKey !== "string" || chatKey === "" || typeof recordedAt !== "string" || !Number.isFinite(Date.parse(recordedAt))) {
|
|
@@ -126354,19 +126394,41 @@ var foldConclusionEvent = (state, name, data) => {
|
|
|
126354
126394
|
var makeConclusionLedger = (core) => {
|
|
126355
126395
|
const inFlight = /* @__PURE__ */ new Set();
|
|
126356
126396
|
const registering = /* @__PURE__ */ new Map();
|
|
126357
|
-
const
|
|
126397
|
+
const unpersisted = /* @__PURE__ */ new Set();
|
|
126398
|
+
const delivered = (key) => core.state.deliveredConclusions?.[key] === true;
|
|
126358
126399
|
let disposed = false;
|
|
126359
126400
|
core.host.onDispose(() => {
|
|
126360
126401
|
disposed = true;
|
|
126361
126402
|
});
|
|
126362
|
-
const settle = (debt) => {
|
|
126363
|
-
delivered.set(keyOf(debt.threadId, debt.userMessageId), true);
|
|
126403
|
+
const settle = ({ debt, turnUserMessageId }) => {
|
|
126364
126404
|
core.commit("conclusion-delivered", {
|
|
126365
126405
|
threadId: debt.threadId,
|
|
126366
|
-
userMessageId: debt.userMessageId
|
|
126406
|
+
userMessageId: debt.userMessageId,
|
|
126407
|
+
...turnUserMessageId !== void 0 && turnUserMessageId !== debt.userMessageId ? { turnUserMessageId } : {}
|
|
126367
126408
|
});
|
|
126368
126409
|
};
|
|
126410
|
+
const forExecutionTurn = (snapshot, threadId, turnId, grouped) => {
|
|
126411
|
+
if (disposed) return { selection: void 0, handled: true };
|
|
126412
|
+
const pending = conclusionDebtsOf(core.state);
|
|
126413
|
+
const candidates = (grouped ?? groupConclusionDebts(snapshot, threadId, Object.values(pending))).byTurn.get(turnId);
|
|
126414
|
+
const currentCandidates = candidates?.filter(
|
|
126415
|
+
(debt) => pending[keyOf(threadId, debt.userMessageId)] !== void 0
|
|
126416
|
+
);
|
|
126417
|
+
if (currentCandidates?.length === 0) return { selection: void 0, handled: true };
|
|
126418
|
+
const selected = conclusionDebtForTurn(
|
|
126419
|
+
snapshot,
|
|
126420
|
+
turnId,
|
|
126421
|
+
currentCandidates ?? [],
|
|
126422
|
+
(messageId) => delivered(keyOf(threadId, messageId))
|
|
126423
|
+
);
|
|
126424
|
+
for (const debt of selected.redundant) settle({ debt, turnUserMessageId: void 0 });
|
|
126425
|
+
return {
|
|
126426
|
+
selection: selected.debt === void 0 ? void 0 : { debt: selected.debt, turnUserMessageId: selected.userMessageId },
|
|
126427
|
+
handled: selected.handled || selected.debt !== void 0 && inFlight.has(keyOf(threadId, selected.debt.userMessageId))
|
|
126428
|
+
};
|
|
126429
|
+
};
|
|
126369
126430
|
return {
|
|
126431
|
+
forExecutionTurn,
|
|
126370
126432
|
starting: (threadId) => {
|
|
126371
126433
|
const gate = Promise.withResolvers();
|
|
126372
126434
|
const pending = registering.get(threadId) ?? /* @__PURE__ */ new Set();
|
|
@@ -126378,44 +126440,59 @@ var makeConclusionLedger = (core) => {
|
|
|
126378
126440
|
gate.resolve();
|
|
126379
126441
|
};
|
|
126380
126442
|
},
|
|
126381
|
-
|
|
126443
|
+
/** 重放才传 recover;只对本次成功补出的欠账调度恢复,已有欠账交给原来的收尾路径。 */
|
|
126444
|
+
owe: async (debt, recover) => {
|
|
126445
|
+
const key = keyOf(debt.threadId, debt.userMessageId);
|
|
126446
|
+
if (delivered(key)) {
|
|
126447
|
+
core.host.log.info("chat skipped a conclusion debt already delivered", { ...debt });
|
|
126448
|
+
return;
|
|
126449
|
+
}
|
|
126382
126450
|
const pending = conclusionDebtsOf(core.state);
|
|
126383
|
-
|
|
126451
|
+
const added = pending[key] === void 0 || unpersisted.has(key);
|
|
126452
|
+
if (Object.keys(pending).length >= CONCLUSION_DEBT_LIMIT && pending[key] === void 0) {
|
|
126384
126453
|
core.host.log.warn("chat dropped its oldest conclusion debt at capacity", {
|
|
126385
126454
|
oldest: Object.values(pending)[0],
|
|
126386
126455
|
limit: CONCLUSION_DEBT_LIMIT
|
|
126387
126456
|
});
|
|
126388
126457
|
}
|
|
126389
|
-
|
|
126458
|
+
if (added) unpersisted.add(key);
|
|
126459
|
+
await core.commitDurably("conclusion-owed", { ...debt });
|
|
126460
|
+
unpersisted.delete(key);
|
|
126461
|
+
if (added) await recover?.(debt);
|
|
126390
126462
|
},
|
|
126391
126463
|
forTurn: async (threadId, turnId = null) => {
|
|
126392
126464
|
await Promise.all(registering.get(threadId) ?? []);
|
|
126393
126465
|
const execution = await core.host.projections.getThreadExecution(threadId);
|
|
126394
|
-
const userMessageId = execution === null ? void 0 : turnId === null ? execution.latestTurn?.userMessageId : userMessageIdOfTurn(execution, turnId);
|
|
126395
|
-
const key = userMessageId === void 0 ? void 0 : keyOf(threadId, userMessageId);
|
|
126396
126466
|
return {
|
|
126397
|
-
|
|
126398
|
-
|
|
126399
|
-
|
|
126467
|
+
execution,
|
|
126468
|
+
...execution === null ? { selection: void 0, handled: false } : forExecutionTurn(
|
|
126469
|
+
indexExecution(execution),
|
|
126470
|
+
threadId,
|
|
126471
|
+
turnId ?? execution.latestTurn?.turnId ?? null
|
|
126472
|
+
)
|
|
126400
126473
|
};
|
|
126401
126474
|
},
|
|
126402
|
-
deliver: async (
|
|
126475
|
+
deliver: async (selection, send) => {
|
|
126476
|
+
const { debt } = selection;
|
|
126403
126477
|
const key = keyOf(debt.threadId, debt.userMessageId);
|
|
126404
|
-
if (disposed || inFlight.has(key) || conclusionDebtsOf(core.state)[key] === void 0)
|
|
126478
|
+
if (disposed || delivered(key) || inFlight.has(key) || conclusionDebtsOf(core.state)[key] === void 0)
|
|
126405
126479
|
return false;
|
|
126406
126480
|
inFlight.add(key);
|
|
126407
126481
|
try {
|
|
126408
|
-
const sent = await send(
|
|
126409
|
-
|
|
126482
|
+
const sent = await send(
|
|
126483
|
+
() => !delivered(key) && conclusionDebtsOf(core.state)[key] !== void 0
|
|
126484
|
+
);
|
|
126485
|
+
if (sent) settle(selection);
|
|
126410
126486
|
return sent;
|
|
126411
126487
|
} finally {
|
|
126412
126488
|
inFlight.delete(key);
|
|
126413
126489
|
}
|
|
126414
126490
|
},
|
|
126415
|
-
discard: (
|
|
126491
|
+
discard: (selection) => {
|
|
126492
|
+
const { debt } = selection;
|
|
126416
126493
|
const key = keyOf(debt.threadId, debt.userMessageId);
|
|
126417
|
-
if (!disposed && !inFlight.has(key) && conclusionDebtsOf(core.state)[key] !== void 0) {
|
|
126418
|
-
settle(
|
|
126494
|
+
if (!disposed && !delivered(key) && !inFlight.has(key) && conclusionDebtsOf(core.state)[key] !== void 0) {
|
|
126495
|
+
settle(selection);
|
|
126419
126496
|
}
|
|
126420
126497
|
}
|
|
126421
126498
|
};
|
|
@@ -127430,6 +127507,26 @@ var makeChatQueue = (core) => {
|
|
|
127430
127507
|
// src/chatSetup.ts
|
|
127431
127508
|
import { threadOrNull } from "@ycodium-ai/plugin-api";
|
|
127432
127509
|
|
|
127510
|
+
// src/boundedMap.ts
|
|
127511
|
+
var makeBoundedMap = (limit) => {
|
|
127512
|
+
const entries = /* @__PURE__ */ new Map();
|
|
127513
|
+
return {
|
|
127514
|
+
get: (key) => entries.get(key),
|
|
127515
|
+
set: (key, value2) => {
|
|
127516
|
+
entries.delete(key);
|
|
127517
|
+
entries.set(key, value2);
|
|
127518
|
+
if (entries.size > limit) {
|
|
127519
|
+
const oldest = entries.keys().next().value;
|
|
127520
|
+
if (oldest !== void 0) entries.delete(oldest);
|
|
127521
|
+
}
|
|
127522
|
+
},
|
|
127523
|
+
/** 忘掉键满足条件的那些。 */
|
|
127524
|
+
forget: (matches) => {
|
|
127525
|
+
for (const key of [...entries.keys()]) if (matches(key)) entries.delete(key);
|
|
127526
|
+
}
|
|
127527
|
+
};
|
|
127528
|
+
};
|
|
127529
|
+
|
|
127433
127530
|
// src/cooldown.ts
|
|
127434
127531
|
var makeCooldown = (windowMs, now, maxKeys = Number.POSITIVE_INFINITY) => {
|
|
127435
127532
|
const lastAt = /* @__PURE__ */ new Map();
|
|
@@ -133584,18 +133681,14 @@ var makeInboundStore = (input) => {
|
|
|
133584
133681
|
write(rowId, row);
|
|
133585
133682
|
return result2.promise;
|
|
133586
133683
|
};
|
|
133587
|
-
const
|
|
133684
|
+
const claimReceipt = (rowId, id, message, receivedAt = 0) => {
|
|
133588
133685
|
const row = rowOf(rowId);
|
|
133589
|
-
|
|
133590
|
-
try {
|
|
133591
|
-
assertReadable(row);
|
|
133592
|
-
} catch (error) {
|
|
133593
|
-
return Promise.reject(error);
|
|
133594
|
-
}
|
|
133686
|
+
assertReadable(row);
|
|
133595
133687
|
const pending = row.claims.get(id);
|
|
133596
|
-
if (pending !== void 0) return
|
|
133688
|
+
if (pending !== void 0) return { first: false, persisted: pending };
|
|
133597
133689
|
const seen = row.data.seenIds.includes(id);
|
|
133598
|
-
if (seen && (row.claimRevisions.get(id) ?? 0) <= row.persisted)
|
|
133690
|
+
if (seen && (row.claimRevisions.get(id) ?? 0) <= row.persisted)
|
|
133691
|
+
return { first: false, persisted: Promise.resolve() };
|
|
133599
133692
|
if (!seen) {
|
|
133600
133693
|
if (message !== void 0) {
|
|
133601
133694
|
const inbox = row.data.inbox ?? [];
|
|
@@ -133639,7 +133732,17 @@ var makeInboundStore = (input) => {
|
|
|
133639
133732
|
row.claims.delete(id);
|
|
133640
133733
|
};
|
|
133641
133734
|
void result2.then(forget, forget);
|
|
133642
|
-
return
|
|
133735
|
+
return { first: !seen, persisted: result2 };
|
|
133736
|
+
};
|
|
133737
|
+
const claim = (rowId, id, message, receivedAt = 0) => {
|
|
133738
|
+
const row = rowOf(rowId);
|
|
133739
|
+
if (!row.loaded) return load(rowId).then(() => claim(rowId, id, message, receivedAt));
|
|
133740
|
+
try {
|
|
133741
|
+
const receipt = claimReceipt(rowId, id, message, receivedAt);
|
|
133742
|
+
return receipt.persisted.then(() => receipt.first);
|
|
133743
|
+
} catch (error) {
|
|
133744
|
+
return Promise.reject(error);
|
|
133745
|
+
}
|
|
133643
133746
|
};
|
|
133644
133747
|
const removeEntry = (rowId, key, failure2) => {
|
|
133645
133748
|
const row = rowOf(rowId);
|
|
@@ -133765,6 +133868,7 @@ var makeInboundStore = (input) => {
|
|
|
133765
133868
|
return {
|
|
133766
133869
|
load,
|
|
133767
133870
|
claim,
|
|
133871
|
+
claimReceipt,
|
|
133768
133872
|
get: (rowId, key) => {
|
|
133769
133873
|
const row = rowOf(rowId);
|
|
133770
133874
|
assertReadable(row);
|
|
@@ -133802,6 +133906,8 @@ var makeInboundStore = (input) => {
|
|
|
133802
133906
|
assertReadable(row);
|
|
133803
133907
|
if (row.data.inbox !== void 0)
|
|
133804
133908
|
row.data.inbox = row.data.inbox.filter((e) => e.key !== key);
|
|
133909
|
+
row.data.seenIds = row.data.seenIds.filter((id) => id !== key);
|
|
133910
|
+
row.claimRevisions.delete(key);
|
|
133805
133911
|
row.revision++;
|
|
133806
133912
|
},
|
|
133807
133913
|
reset,
|
|
@@ -133942,7 +134048,9 @@ var startConnections = (core, inboundHandlersFor, inboundState, recovery) => {
|
|
|
133942
134048
|
...inbound,
|
|
133943
134049
|
// 停用先封接收,保持发送连接直到已接收工作 drain 完成。
|
|
133944
134050
|
onMessage: (message) => {
|
|
133945
|
-
if (
|
|
134051
|
+
if (closed || disposed)
|
|
134052
|
+
return Promise.reject(new Error("inbound connection is closed"));
|
|
134053
|
+
return inbound.onMessage(message);
|
|
133946
134054
|
},
|
|
133947
134055
|
onButton: (...args2) => closed || disposed ? null : inbound.onButton(...args2),
|
|
133948
134056
|
onPanelClick: (click) => closed || disposed ? null : inbound.onPanelClick?.(click) ?? null,
|
|
@@ -134339,16 +134447,18 @@ var makeImCore = (input) => {
|
|
|
134339
134447
|
}
|
|
134340
134448
|
);
|
|
134341
134449
|
let persisting = Promise.resolve();
|
|
134342
|
-
const
|
|
134450
|
+
const commitDurably = (name, data) => {
|
|
134343
134451
|
state = foldImEvent(state, name, data);
|
|
134344
|
-
|
|
134452
|
+
const saved = persisting.then(
|
|
134345
134453
|
() => host.events.append({ name, aggregate: { kind: "plugin", id: input.pluginId }, data })
|
|
134346
|
-
).then(
|
|
134347
|
-
|
|
134348
|
-
(error)
|
|
134349
|
-
|
|
134350
|
-
|
|
134351
|
-
|
|
134454
|
+
).then(() => void 0);
|
|
134455
|
+
persisting = saved.catch((error) => {
|
|
134456
|
+
host.log.warn("chat could not save its state", { name, error: describeError(error) });
|
|
134457
|
+
});
|
|
134458
|
+
return saved;
|
|
134459
|
+
};
|
|
134460
|
+
const commit = (name, data) => {
|
|
134461
|
+
void commitDurably(name, data);
|
|
134352
134462
|
};
|
|
134353
134463
|
const bindChat = (chatKey, threadId) => {
|
|
134354
134464
|
commit("chat-updated", { chatKey, threadId });
|
|
@@ -134414,6 +134524,7 @@ var makeImCore = (input) => {
|
|
|
134414
134524
|
},
|
|
134415
134525
|
ready,
|
|
134416
134526
|
commit,
|
|
134527
|
+
commitDurably,
|
|
134417
134528
|
bindChat,
|
|
134418
134529
|
outbound,
|
|
134419
134530
|
deliver,
|
|
@@ -134885,6 +134996,7 @@ import { projectOrNull } from "@ycodium-ai/plugin-api";
|
|
|
134885
134996
|
|
|
134886
134997
|
// src/inboxProcessing.ts
|
|
134887
134998
|
var processingFor = (store, rowId, entry, retry) => ({
|
|
134999
|
+
replayed: entry.replayed === true,
|
|
134888
135000
|
get entry() {
|
|
134889
135001
|
return store.get(rowId, entry.key);
|
|
134890
135002
|
},
|
|
@@ -135046,7 +135158,7 @@ var makeInboxReplay = (core, currentBot, state, enqueueForChat, resumeMessage, c
|
|
|
135046
135158
|
await resumeMessage(
|
|
135047
135159
|
bot,
|
|
135048
135160
|
latest.message === entry.message ? message : restoreInbound(core.adapterFor(bot.platform), latest.message),
|
|
135049
|
-
latest,
|
|
135161
|
+
{ ...latest, replayed: true },
|
|
135050
135162
|
chatId
|
|
135051
135163
|
);
|
|
135052
135164
|
});
|
|
@@ -135085,6 +135197,45 @@ var makeGreetings = (core, enqueueForChat, now) => {
|
|
|
135085
135197
|
};
|
|
135086
135198
|
};
|
|
135087
135199
|
|
|
135200
|
+
// src/replyAnchors.ts
|
|
135201
|
+
var THREAD_LIMIT = 256;
|
|
135202
|
+
var TURNS_PER_THREAD = 16;
|
|
135203
|
+
var anchorOfMessage = (adapter, message) => adapter?.quotesReplies === true ? { messageId: message.messageId, inThread: message.replyInThread === true } : void 0;
|
|
135204
|
+
var makeReplyAnchors = (host) => {
|
|
135205
|
+
const byThread = makeBoundedMap(THREAD_LIMIT);
|
|
135206
|
+
const userMessageOfTurn = async (threadId, turnId) => {
|
|
135207
|
+
const execution = await host.projections.getThreadExecution(threadId).catch(() => null);
|
|
135208
|
+
if (execution === null) return void 0;
|
|
135209
|
+
const wanted = turnId ?? execution.session?.activeTurnId ?? execution.latestTurn?.turnId;
|
|
135210
|
+
return wanted == null ? void 0 : executionTurnFor(indexExecution(execution), wanted)?.userMessageId;
|
|
135211
|
+
};
|
|
135212
|
+
return {
|
|
135213
|
+
/**
|
|
135214
|
+
* 开轮时记一笔:`userMessageId` 是派给宿主的那条用户消息,`binding` 是收到聊天消息的那个
|
|
135215
|
+
* 聊天(带着它的锚点)。没有锚点(平台不引用)就不记。
|
|
135216
|
+
*/
|
|
135217
|
+
note: (threadId, userMessageId, binding) => {
|
|
135218
|
+
if (binding.reply === void 0) return;
|
|
135219
|
+
const turns = byThread.get(threadId) ?? makeBoundedMap(TURNS_PER_THREAD);
|
|
135220
|
+
byThread.set(threadId, turns);
|
|
135221
|
+
turns.set(userMessageId, { chatKey: chatKeyOf(binding), anchor: binding.reply });
|
|
135222
|
+
},
|
|
135223
|
+
/**
|
|
135224
|
+
* 给发往这条会话的聊天挂上 `turnId` 那一轮的锚点(null 的含义见上);取不到就原样返回。
|
|
135225
|
+
*
|
|
135226
|
+
* 锚点只配给它那条消息所在的聊天:会话可以被另一个聊天接走(`/resume`),而回复接口只按被
|
|
135227
|
+
* 回复的消息寻址,挂错了答复就发回原来的聊天。
|
|
135228
|
+
*/
|
|
135229
|
+
anchored: async (binding, threadId, turnId) => {
|
|
135230
|
+
const turns = byThread.get(threadId);
|
|
135231
|
+
if (turns === void 0) return binding;
|
|
135232
|
+
const asked = await userMessageOfTurn(threadId, turnId);
|
|
135233
|
+
const noted = asked === void 0 ? void 0 : turns.get(asked);
|
|
135234
|
+
return noted?.chatKey === chatKeyOf(binding) ? { ...binding, reply: noted.anchor } : binding;
|
|
135235
|
+
}
|
|
135236
|
+
};
|
|
135237
|
+
};
|
|
135238
|
+
|
|
135088
135239
|
// src/inbound.ts
|
|
135089
135240
|
var ARCHIVED_REPLY_COOLDOWN_MS = 10 * 6e4;
|
|
135090
135241
|
var UNSUPPORTED_REPLY_COOLDOWN_MS = 6e4;
|
|
@@ -135312,40 +135463,44 @@ var makeInbound = (core, deps) => {
|
|
|
135312
135463
|
noteChatAlias: (alias, chatId) => noteChatAlias(bot, alias, chatId),
|
|
135313
135464
|
knowsChatAlias: (alias) => chatAliasOf(core.state, bot.rowId, alias) !== void 0,
|
|
135314
135465
|
onGreeting: (chatId, senderId) => greet(bot.rowId, chatId, senderId),
|
|
135315
|
-
onMessage: (received) => {
|
|
135466
|
+
onMessage: async (received) => {
|
|
135316
135467
|
const current = currentBot(bot.rowId);
|
|
135317
|
-
if (!accepting || current === void 0)
|
|
135468
|
+
if (!accepting || current === void 0) throw new Error("inbound is not accepting messages");
|
|
135318
135469
|
const message = settleChat(current, received);
|
|
135319
135470
|
const work = workForMessage(message);
|
|
135320
|
-
|
|
135471
|
+
const adapter = adapterFor(current.platform);
|
|
135321
135472
|
let saved;
|
|
135322
135473
|
try {
|
|
135323
|
-
saved = persistInbound(
|
|
135474
|
+
saved = persistInbound(adapter, message);
|
|
135324
135475
|
} catch (error) {
|
|
135325
135476
|
host.log.warn("chat could not serialize inbound; processing without restart protection", {
|
|
135326
135477
|
rowId: current.rowId,
|
|
135327
135478
|
error: describeError(error)
|
|
135328
135479
|
});
|
|
135329
135480
|
}
|
|
135481
|
+
const claim = inboundState.claimReceipt(current.rowId, work.key, saved, now());
|
|
135482
|
+
if (!claim.first) return claim.persisted;
|
|
135483
|
+
const receipt = claim.persisted.catch((error) => {
|
|
135484
|
+
inboundState.abandon(current.rowId, work.key);
|
|
135485
|
+
host.log.warn(
|
|
135486
|
+
adapter?.redeliversUnacknowledged ? "chat could not save inbound state; waiting for platform redelivery" : "chat could not save inbound state; processing without restart protection",
|
|
135487
|
+
{ rowId: current.rowId, error: describeError(error) }
|
|
135488
|
+
);
|
|
135489
|
+
throw error;
|
|
135490
|
+
});
|
|
135491
|
+
const enqueue = () => {
|
|
135492
|
+
const arrived = bindingOf2(current, message.chatId);
|
|
135493
|
+
if (message.chatKind !== void 0 && chatOf(core.state, chatKeyOf(arrived)).chatKind !== message.chatKind) {
|
|
135494
|
+
commit("chat-updated", { chatKey: chatKeyOf(arrived), chatKind: message.chatKind });
|
|
135495
|
+
}
|
|
135496
|
+
enqueueForChat(chatKeyOf(arrived), () => resumeMessage(current, message, work));
|
|
135497
|
+
};
|
|
135330
135498
|
track(
|
|
135331
|
-
|
|
135332
|
-
()
|
|
135333
|
-
},
|
|
135334
|
-
(error) => {
|
|
135335
|
-
inboundState.abandon(current.rowId, work.key);
|
|
135336
|
-
host.log.warn(
|
|
135337
|
-
"chat could not save inbound state; processing without restart protection",
|
|
135338
|
-
{ rowId: current.rowId, error: describeError(error) }
|
|
135339
|
-
);
|
|
135340
|
-
}
|
|
135341
|
-
).then(() => {
|
|
135342
|
-
const arrived = bindingOf2(current, message.chatId);
|
|
135343
|
-
if (message.chatKind !== void 0 && chatOf(core.state, chatKeyOf(arrived)).chatKind !== message.chatKind) {
|
|
135344
|
-
commit("chat-updated", { chatKey: chatKeyOf(arrived), chatKind: message.chatKind });
|
|
135345
|
-
}
|
|
135346
|
-
enqueueForChat(chatKeyOf(arrived), () => resumeMessage(current, message, work));
|
|
135499
|
+
receipt.then(enqueue, () => {
|
|
135500
|
+
if (!adapter?.redeliversUnacknowledged) enqueue();
|
|
135347
135501
|
})
|
|
135348
135502
|
);
|
|
135503
|
+
return receipt;
|
|
135349
135504
|
}
|
|
135350
135505
|
});
|
|
135351
135506
|
const replay = makeInboxReplay(
|
|
@@ -135779,14 +135934,6 @@ var queueCardOf = (record4, view, note, t) => {
|
|
|
135779
135934
|
var queueViewKey = (view, note) => JSON.stringify([view, note]);
|
|
135780
135935
|
|
|
135781
135936
|
// src/queueFlow.ts
|
|
135782
|
-
var LIVE_STATUSES = /* @__PURE__ */ new Set([
|
|
135783
|
-
"queued",
|
|
135784
|
-
"preparing",
|
|
135785
|
-
"dispatching",
|
|
135786
|
-
"running",
|
|
135787
|
-
"reconciling",
|
|
135788
|
-
"blocked"
|
|
135789
|
-
]);
|
|
135790
135937
|
var makeQueueFlow = (host, deps) => {
|
|
135791
135938
|
const { t } = host;
|
|
135792
135939
|
const shown = /* @__PURE__ */ new Map();
|
|
@@ -135810,7 +135957,7 @@ var makeQueueFlow = (host, deps) => {
|
|
|
135810
135957
|
const intent = execution?.intents.find((entry) => entry.messageId === messageId);
|
|
135811
135958
|
if (execution === null || intent === void 0 || intent.status !== "queued") return;
|
|
135812
135959
|
const waiting = execution.activeTurnId !== null || execution.queuePaused || execution.intents.some(
|
|
135813
|
-
(other) => other.intentId !== intent.intentId &&
|
|
135960
|
+
(other) => other.intentId !== intent.intentId && isLiveIntentStatus(other.status) && other.sequence < intent.sequence
|
|
135814
135961
|
);
|
|
135815
135962
|
if (!waiting) return;
|
|
135816
135963
|
const cards = target2.adapter.choiceCards;
|
|
@@ -144047,7 +144194,7 @@ var cardActionOperator = (event) => {
|
|
|
144047
144194
|
};
|
|
144048
144195
|
var startLarkInbound = (input) => {
|
|
144049
144196
|
const dispatcher = new import_node_sdk.EventDispatcher({}).register({
|
|
144050
|
-
"im.message.receive_v1": (event) => {
|
|
144197
|
+
"im.message.receive_v1": async (event) => {
|
|
144051
144198
|
const mode = input.groupMention ?? "always";
|
|
144052
144199
|
const parse = (groupMention) => parseInboundMessage(
|
|
144053
144200
|
event,
|
|
@@ -144057,17 +144204,16 @@ var startLarkInbound = (input) => {
|
|
|
144057
144204
|
);
|
|
144058
144205
|
const parsed = parse(mode);
|
|
144059
144206
|
if (parsed !== null) {
|
|
144060
|
-
input.onMessage(parsed);
|
|
144207
|
+
await input.onMessage(parsed);
|
|
144061
144208
|
return {};
|
|
144062
144209
|
}
|
|
144063
144210
|
const chat = event.message;
|
|
144064
144211
|
if (mode === "always" && input.isSoloGroup !== void 0 && chat?.chat_type !== "p2p") {
|
|
144065
144212
|
const chatId = chat?.chat_id;
|
|
144066
144213
|
if (typeof chatId === "string") {
|
|
144067
|
-
|
|
144068
|
-
|
|
144069
|
-
|
|
144070
|
-
});
|
|
144214
|
+
const solo = await input.isSoloGroup(chatId).catch(() => false);
|
|
144215
|
+
const relaxed = solo ? parse("never") : null;
|
|
144216
|
+
if (relaxed !== null) await input.onMessage(relaxed);
|
|
144071
144217
|
}
|
|
144072
144218
|
}
|
|
144073
144219
|
return {};
|
|
@@ -144646,10 +144792,12 @@ var makeLarkAdapter = (deps) => {
|
|
|
144646
144792
|
};
|
|
144647
144793
|
const soloGroupFor = (context2) => async (chatId) => {
|
|
144648
144794
|
const info = await context2.chatInfo(chatId);
|
|
144649
|
-
|
|
144795
|
+
if (info?.userCount === void 0 || info.botCount === void 0) return false;
|
|
144796
|
+
return info.userCount <= 1 && info.botCount <= 1;
|
|
144650
144797
|
};
|
|
144651
144798
|
return {
|
|
144652
144799
|
platform: "lark",
|
|
144800
|
+
redeliversUnacknowledged: true,
|
|
144653
144801
|
...larkInboundPersistence((rowId) => messageContexts.get(rowId)?.context),
|
|
144654
144802
|
botIdentity: (bot) => larkCredentials(bot).appId,
|
|
144655
144803
|
icon: "lark",
|
|
@@ -144757,15 +144905,27 @@ var makeLarkAdapter = (deps) => {
|
|
|
144757
144905
|
const key = JSON.stringify([message.chatId, topic?.threadId]);
|
|
144758
144906
|
const earlier = pendingDeliveries.get(key);
|
|
144759
144907
|
if (earlier === void 0 && pending === null && reference2 === null && !(prepared instanceof Promise) && !(source instanceof Promise)) {
|
|
144760
|
-
deliver(prepared, source);
|
|
144908
|
+
return deliver(prepared, source);
|
|
144761
144909
|
} else {
|
|
144762
|
-
const completed = Promise.all([
|
|
144910
|
+
const completed = Promise.all([
|
|
144911
|
+
earlier?.catch(() => {
|
|
144912
|
+
}),
|
|
144913
|
+
pending,
|
|
144914
|
+
reference2,
|
|
144915
|
+
source,
|
|
144916
|
+
prepared
|
|
144917
|
+
]).then(([, , replyTo, names, ready]) => {
|
|
144763
144918
|
if (pendingDeliveries.get(key) === completed) pendingDeliveries.delete(key);
|
|
144764
|
-
if (open)
|
|
144765
|
-
|
|
144919
|
+
if (!open) throw new Error("lark inbound connection is closed");
|
|
144920
|
+
return deliver(ready, names, replyTo ?? void 0);
|
|
144921
|
+
}).catch((error) => {
|
|
144922
|
+
handlers.onError(describeError(error));
|
|
144923
|
+
throw error;
|
|
144924
|
+
}).finally(() => {
|
|
144766
144925
|
if (pendingDeliveries.get(key) === completed) pendingDeliveries.delete(key);
|
|
144767
144926
|
});
|
|
144768
144927
|
pendingDeliveries.set(key, completed);
|
|
144928
|
+
return completed;
|
|
144769
144929
|
}
|
|
144770
144930
|
}
|
|
144771
144931
|
});
|
|
@@ -146619,18 +146779,18 @@ var makeModelPlan = (core, deps) => {
|
|
|
146619
146779
|
import { readThreadFailed, threadOrNull as threadOrNull5 } from "@ycodium-ai/plugin-api";
|
|
146620
146780
|
|
|
146621
146781
|
// src/conclusionOutbox.ts
|
|
146622
|
-
var enqueueConclusion = (core, ledger, binding,
|
|
146782
|
+
var enqueueConclusion = (core, ledger, binding, selection, send) => core.outbound(
|
|
146623
146783
|
chatKeyOf(binding),
|
|
146624
146784
|
async () => {
|
|
146625
|
-
if (
|
|
146626
|
-
else await ledger.deliver(
|
|
146785
|
+
if (selection === void 0) await send();
|
|
146786
|
+
else await ledger.deliver(selection, send);
|
|
146627
146787
|
},
|
|
146628
|
-
{ droppable:
|
|
146788
|
+
{ droppable: selection !== void 0 }
|
|
146629
146789
|
);
|
|
146630
146790
|
|
|
146631
146791
|
// src/conclusionRecovery.ts
|
|
146632
146792
|
var MAX_AGE_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
146633
|
-
var
|
|
146793
|
+
var makeConclusionRecovery = (core, ledger, delivery, failures) => {
|
|
146634
146794
|
let disposed = false;
|
|
146635
146795
|
core.host.onDispose(() => {
|
|
146636
146796
|
disposed = true;
|
|
@@ -146641,55 +146801,103 @@ var recoverConclusions = async (core, ledger, delivery, failures) => {
|
|
|
146641
146801
|
error: describeError(error)
|
|
146642
146802
|
});
|
|
146643
146803
|
};
|
|
146644
|
-
|
|
146645
|
-
|
|
146804
|
+
const recoverThread = async (threadId, debts, requestedMessageId) => {
|
|
146805
|
+
await core.ready;
|
|
146646
146806
|
if (disposed) return;
|
|
146647
146807
|
try {
|
|
146648
|
-
const execution = await core.host.projections.getThreadExecution(
|
|
146808
|
+
const execution = await core.host.projections.getThreadExecution(threadId);
|
|
146649
146809
|
if (disposed) return;
|
|
146650
146810
|
if (execution === null) {
|
|
146651
|
-
|
|
146652
|
-
|
|
146653
|
-
|
|
146654
|
-
|
|
146655
|
-
|
|
146656
|
-
core.host.log.info("chat discarded a conclusion whose turn is no longer latest", {
|
|
146657
|
-
...debt
|
|
146658
|
-
});
|
|
146659
|
-
ledger.discard(debt);
|
|
146660
|
-
continue;
|
|
146661
|
-
}
|
|
146662
|
-
if (latest.state === "running" || execution.session?.activeTurnId != null) continue;
|
|
146663
|
-
if (latest.completedAt !== null && Date.parse(latest.completedAt) < Date.now() - MAX_AGE_MS) {
|
|
146664
|
-
core.host.log.info("chat discarded a conclusion older than seven days", { ...debt });
|
|
146665
|
-
ledger.discard(debt);
|
|
146666
|
-
continue;
|
|
146811
|
+
for (const debt of debts) {
|
|
146812
|
+
if (requestedMessageId === void 0 || debt.userMessageId === requestedMessageId)
|
|
146813
|
+
ledger.discard({ debt, turnUserMessageId: void 0 });
|
|
146814
|
+
}
|
|
146815
|
+
return;
|
|
146667
146816
|
}
|
|
146668
|
-
const
|
|
146669
|
-
|
|
146670
|
-
|
|
146671
|
-
|
|
146672
|
-
|
|
146673
|
-
|
|
146674
|
-
|
|
146675
|
-
debt
|
|
146676
|
-
|
|
146677
|
-
|
|
146678
|
-
latest.turnId,
|
|
146679
|
-
owed
|
|
146680
|
-
);
|
|
146817
|
+
const snapshot = indexExecution(execution);
|
|
146818
|
+
const grouped = groupConclusionDebts(snapshot, threadId, debts);
|
|
146819
|
+
const { resolved, byTurn } = grouped;
|
|
146820
|
+
for (const { debt, result: result2 } of resolved.values()) {
|
|
146821
|
+
if (result2.kind === "gone") {
|
|
146822
|
+
if (requestedMessageId === void 0 || debt.userMessageId === requestedMessageId) {
|
|
146823
|
+
core.host.log.info("chat discarded a conclusion whose message has no turn", {
|
|
146824
|
+
...debt
|
|
146825
|
+
});
|
|
146826
|
+
ledger.discard({ debt, turnUserMessageId: void 0 });
|
|
146681
146827
|
}
|
|
146682
|
-
const send = await delivery.prepareConclusion(debt.threadId, latest.turnId);
|
|
146683
|
-
return send(binding, { recovered: true, streaming: false, owed });
|
|
146684
|
-
} catch (error) {
|
|
146685
|
-
couldNotRecover(debt, error);
|
|
146686
|
-
return false;
|
|
146687
146828
|
}
|
|
146688
|
-
}
|
|
146829
|
+
}
|
|
146830
|
+
const requested = requestedMessageId === void 0 ? void 0 : resolved.get(requestedMessageId)?.result;
|
|
146831
|
+
for (const turnId of byTurn.keys()) {
|
|
146832
|
+
if (requestedMessageId !== void 0 && (requested?.kind !== "settled" && requested?.kind !== "merged" || requested.turnId !== turnId))
|
|
146833
|
+
continue;
|
|
146834
|
+
const { selection, handled } = ledger.forExecutionTurn(snapshot, threadId, turnId, grouped);
|
|
146835
|
+
if (handled || selection === void 0 || requestedMessageId !== void 0 && selection.debt.userMessageId !== requestedMessageId)
|
|
146836
|
+
continue;
|
|
146837
|
+
const result2 = resolved.get(selection.debt.userMessageId).result;
|
|
146838
|
+
if (result2.kind === "pending" || result2.kind === "gone") continue;
|
|
146839
|
+
recoverTurn(selection, result2.kind === "settled" ? result2 : result2.turn, execution);
|
|
146840
|
+
}
|
|
146689
146841
|
} catch (error) {
|
|
146690
|
-
couldNotRecover(debt, error);
|
|
146842
|
+
for (const debt of debts) couldNotRecover(debt, error);
|
|
146691
146843
|
}
|
|
146692
|
-
}
|
|
146844
|
+
};
|
|
146845
|
+
const recoverTurn = (selection, turn, execution) => {
|
|
146846
|
+
const { debt } = selection;
|
|
146847
|
+
if (turn === void 0 || turn.state === "pending" || turn.state === "running" || execution.session?.activeTurnId === turn.turnId)
|
|
146848
|
+
return;
|
|
146849
|
+
if (turn.completedAt !== null && Date.parse(turn.completedAt) < Date.now() - MAX_AGE_MS) {
|
|
146850
|
+
core.host.log.info("chat discarded a conclusion older than seven days", { ...debt });
|
|
146851
|
+
ledger.discard(selection);
|
|
146852
|
+
return;
|
|
146853
|
+
}
|
|
146854
|
+
const binding = core.bindingForChatKey(selection.debt.chatKey);
|
|
146855
|
+
if (binding === void 0) return;
|
|
146856
|
+
enqueueConclusion(core, ledger, binding, selection, async (owed) => {
|
|
146857
|
+
try {
|
|
146858
|
+
if (turn.state === "error") {
|
|
146859
|
+
const detail = execution.latestTurn?.state === "error" && execution.latestTurn.turnId === turn.turnId ? execution.session?.lastError : void 0;
|
|
146860
|
+
return delivery.sendText(
|
|
146861
|
+
binding,
|
|
146862
|
+
debt.threadId,
|
|
146863
|
+
failures.forFailure(debt.threadId, detail),
|
|
146864
|
+
true,
|
|
146865
|
+
turn.turnId,
|
|
146866
|
+
owed
|
|
146867
|
+
);
|
|
146868
|
+
}
|
|
146869
|
+
const send = await delivery.prepareConclusion(debt.threadId, turn.turnId);
|
|
146870
|
+
return send(binding, { recovered: true, streaming: false, owed });
|
|
146871
|
+
} catch (error) {
|
|
146872
|
+
couldNotRecover(debt, error);
|
|
146873
|
+
return false;
|
|
146874
|
+
}
|
|
146875
|
+
});
|
|
146876
|
+
};
|
|
146877
|
+
const recoverConclusion = async (debt) => {
|
|
146878
|
+
await core.ready;
|
|
146879
|
+
await recoverThread(
|
|
146880
|
+
debt.threadId,
|
|
146881
|
+
Object.values(conclusionDebtsOf(core.state)).filter(
|
|
146882
|
+
(candidate) => candidate.threadId === debt.threadId
|
|
146883
|
+
),
|
|
146884
|
+
debt.userMessageId
|
|
146885
|
+
);
|
|
146886
|
+
};
|
|
146887
|
+
const recoverConclusions = async () => {
|
|
146888
|
+
await core.ready;
|
|
146889
|
+
const byThread = /* @__PURE__ */ new Map();
|
|
146890
|
+
for (const debt of Object.values(conclusionDebtsOf(core.state))) {
|
|
146891
|
+
const group = byThread.get(debt.threadId) ?? [];
|
|
146892
|
+
group.push(debt);
|
|
146893
|
+
byThread.set(debt.threadId, group);
|
|
146894
|
+
}
|
|
146895
|
+
for (const [threadId, debts] of byThread) {
|
|
146896
|
+
if (disposed) return;
|
|
146897
|
+
await recoverThread(threadId, debts);
|
|
146898
|
+
}
|
|
146899
|
+
};
|
|
146900
|
+
return { recoverConclusion, recoverConclusions };
|
|
146693
146901
|
};
|
|
146694
146902
|
|
|
146695
146903
|
// src/notificationTargets.ts
|
|
@@ -146713,7 +146921,13 @@ var makeNotifications = (core, deps) => {
|
|
|
146713
146921
|
const { takeWorking } = deps;
|
|
146714
146922
|
const { turnCards } = deps.replyCards;
|
|
146715
146923
|
const { failures, conclusions, delivery } = deps;
|
|
146716
|
-
|
|
146924
|
+
const { recoverConclusion, recoverConclusions } = makeConclusionRecovery(
|
|
146925
|
+
core,
|
|
146926
|
+
conclusions,
|
|
146927
|
+
delivery,
|
|
146928
|
+
failures
|
|
146929
|
+
);
|
|
146930
|
+
void recoverConclusions();
|
|
146717
146931
|
const projectByThread = /* @__PURE__ */ new Map();
|
|
146718
146932
|
const projectOfThread = async (threadId) => {
|
|
146719
146933
|
const known = projectByThread.get(threadId);
|
|
@@ -146761,14 +146975,14 @@ var makeNotifications = (core, deps) => {
|
|
|
146761
146975
|
await ready;
|
|
146762
146976
|
const targets = targetsForProject(threadId, await projectOfThread(threadId));
|
|
146763
146977
|
const owes = Object.values(conclusionDebtsOf(core.state)).some(
|
|
146764
|
-
(
|
|
146978
|
+
(debt) => debt.threadId === threadId
|
|
146765
146979
|
);
|
|
146766
|
-
const {
|
|
146767
|
-
if (
|
|
146768
|
-
const binding = bindingForChatKey(debt.chatKey);
|
|
146980
|
+
const { selection, handled, execution } = said || owes || targets.length > 0 ? await conclusions.forTurn(threadId) : { selection: void 0, handled: false, execution: null };
|
|
146981
|
+
if (selection !== void 0) {
|
|
146982
|
+
const binding = bindingForChatKey(selection.debt.chatKey);
|
|
146769
146983
|
if (binding !== void 0 && !streamsTo(binding, threadId) && execution?.session?.activeTurnId == null && execution?.latestTurn?.state !== "running" && execution?.latestTurn?.state !== "error" && payload.session?.status !== "error") {
|
|
146770
146984
|
const turnId = execution?.latestTurn?.turnId ?? null;
|
|
146771
|
-
enqueueConclusion(core, conclusions, binding,
|
|
146985
|
+
enqueueConclusion(core, conclusions, binding, selection, async (owed) => {
|
|
146772
146986
|
const send = await delivery.prepareConclusion(threadId, turnId);
|
|
146773
146987
|
return send(binding, { recovered: false, streaming: false, owed });
|
|
146774
146988
|
});
|
|
@@ -146803,9 +147017,9 @@ var makeNotifications = (core, deps) => {
|
|
|
146803
147017
|
threadsWithSomethingToReport.delete(threadId);
|
|
146804
147018
|
await ready;
|
|
146805
147019
|
const targets = targetsForProject(threadId, await projectOfThread(threadId));
|
|
146806
|
-
const {
|
|
146807
|
-
const debtBinding =
|
|
146808
|
-
const recipients =
|
|
147020
|
+
const { selection, handled, execution } = await conclusions.forTurn(threadId);
|
|
147021
|
+
const debtBinding = selection === void 0 ? void 0 : bindingForChatKey(selection.debt.chatKey);
|
|
147022
|
+
const recipients = selection !== void 0 ? debtBinding === void 0 ? [] : [debtBinding] : targets;
|
|
146809
147023
|
const textTargets = handled ? [] : recipients.filter(
|
|
146810
147024
|
(binding) => !(streamsTo(binding, threadId) && turnCards.covers(threadId))
|
|
146811
147025
|
);
|
|
@@ -146816,7 +147030,7 @@ var makeNotifications = (core, deps) => {
|
|
|
146816
147030
|
if (notice !== null) {
|
|
146817
147031
|
for (const binding of textTargets) {
|
|
146818
147032
|
const send = (owed) => delivery.sendText(binding, threadId, notice, false, null, owed);
|
|
146819
|
-
enqueueConclusion(core, conclusions, binding,
|
|
147033
|
+
enqueueConclusion(core, conclusions, binding, selection, send);
|
|
146820
147034
|
}
|
|
146821
147035
|
}
|
|
146822
147036
|
for (const binding of targets) {
|
|
@@ -146831,7 +147045,7 @@ var makeNotifications = (core, deps) => {
|
|
|
146831
147045
|
const rememberThreadProject = (threadId, projectId) => {
|
|
146832
147046
|
projectByThread.set(threadId, projectId);
|
|
146833
147047
|
};
|
|
146834
|
-
return { rememberThreadProject };
|
|
147048
|
+
return { rememberThreadProject, recoverConclusion };
|
|
146835
147049
|
};
|
|
146836
147050
|
|
|
146837
147051
|
// src/panelCommands.ts
|
|
@@ -147806,10 +148020,10 @@ var makeReplyCards = (core, failures, anchors, conclusions, delivery) => {
|
|
|
147806
148020
|
onOpened: (card) => commit("turn-card-opened", card),
|
|
147807
148021
|
onClosed: (cardId) => commit("turn-card-closed", { cardId }),
|
|
147808
148022
|
finalize: async (threadId, turnId, chatKey, send) => {
|
|
147809
|
-
const {
|
|
148023
|
+
const { selection, handled } = await conclusions.forTurn(threadId, turnId);
|
|
147810
148024
|
if (handled) return true;
|
|
147811
|
-
if (
|
|
147812
|
-
if (!await conclusions.deliver(
|
|
148025
|
+
if (selection === void 0) return send();
|
|
148026
|
+
if (!await conclusions.deliver(selection, send)) return false;
|
|
147813
148027
|
const binding = bindingForChatKey(chatKey);
|
|
147814
148028
|
if (binding === void 0) return true;
|
|
147815
148029
|
const sendConclusion = await delivery.prepareConclusion(threadId, turnId);
|
|
@@ -147817,7 +148031,7 @@ var makeReplyCards = (core, failures, anchors, conclusions, delivery) => {
|
|
|
147817
148031
|
return true;
|
|
147818
148032
|
},
|
|
147819
148033
|
fallback: async (threadId, turnId, chatKey, view) => {
|
|
147820
|
-
const {
|
|
148034
|
+
const { selection, handled } = await conclusions.forTurn(threadId, turnId);
|
|
147821
148035
|
if (handled) return;
|
|
147822
148036
|
const binding = bindingForChatKey(chatKey);
|
|
147823
148037
|
if (binding === void 0) return;
|
|
@@ -147826,7 +148040,7 @@ var makeReplyCards = (core, failures, anchors, conclusions, delivery) => {
|
|
|
147826
148040
|
core,
|
|
147827
148041
|
conclusions,
|
|
147828
148042
|
binding,
|
|
147829
|
-
|
|
148043
|
+
selection,
|
|
147830
148044
|
(owed) => delivery.sendText(binding, threadId, text4, false, turnId, owed)
|
|
147831
148045
|
);
|
|
147832
148046
|
},
|
|
@@ -148211,7 +148425,7 @@ var teamGoalRequestId = (binding, messageId) => {
|
|
|
148211
148425
|
return NodeCrypto5.createHash("sha256").update(identity2).digest("hex");
|
|
148212
148426
|
};
|
|
148213
148427
|
var makeTeamGoals = (core) => {
|
|
148214
|
-
const { host, t, deliver,
|
|
148428
|
+
const { host, t, deliver, commitDurably } = core;
|
|
148215
148429
|
const hasRosterFor = async (binding) => {
|
|
148216
148430
|
try {
|
|
148217
148431
|
const roster = await host.plugins.invoke({
|
|
@@ -148255,13 +148469,16 @@ var makeTeamGoals = (core) => {
|
|
|
148255
148469
|
throw new Error("team startGoal returned an invalid result");
|
|
148256
148470
|
}
|
|
148257
148471
|
topicRoot?.confirmConsumed();
|
|
148258
|
-
commit("team-goal-started", { chatKey: chatKeyOf(binding), threadId: result2.leadThreadId });
|
|
148259
148472
|
goal = { leadThreadId: result2.leadThreadId, leadName: result2.leadName };
|
|
148260
148473
|
} catch (error) {
|
|
148261
148474
|
host.log.warn("chat could not start the team goal", { error: describeError(error) });
|
|
148262
148475
|
await deliver(binding, t("team.startFailed"));
|
|
148263
148476
|
return null;
|
|
148264
148477
|
}
|
|
148478
|
+
await commitDurably("team-goal-started", {
|
|
148479
|
+
chatKey: chatKeyOf(binding),
|
|
148480
|
+
threadId: goal.leadThreadId
|
|
148481
|
+
});
|
|
148265
148482
|
await accepted?.(goal.leadThreadId);
|
|
148266
148483
|
await deliver(
|
|
148267
148484
|
binding,
|
|
@@ -148955,6 +149172,7 @@ var makeTelegramAdapter = (deps) => {
|
|
|
148955
149172
|
});
|
|
148956
149173
|
return {
|
|
148957
149174
|
platform: "telegram",
|
|
149175
|
+
redeliversUnacknowledged: true,
|
|
148958
149176
|
botIdentity: tokenFingerprint,
|
|
148959
149177
|
icon: "telegram",
|
|
148960
149178
|
label: "Telegram",
|
|
@@ -149066,14 +149284,16 @@ var makeTelegramAdapter = (deps) => {
|
|
|
149066
149284
|
}
|
|
149067
149285
|
}
|
|
149068
149286
|
if (!running) break;
|
|
149069
|
-
advanceOffset(update);
|
|
149070
149287
|
const senderId = message.from === void 0 ? null : String(message.from.id);
|
|
149071
149288
|
const senderName2 = telegramDisplayName(message.from);
|
|
149072
149289
|
const chatKind = message.chat.type === "private" ? "private" : "group";
|
|
149073
149290
|
if (message.voice !== void 0 || text4 === "" && resource === null) {
|
|
149074
149291
|
const unsupported = unsupportedTypeOf(message);
|
|
149075
|
-
if (unsupported === null)
|
|
149076
|
-
|
|
149292
|
+
if (unsupported === null) {
|
|
149293
|
+
advanceOffset(update);
|
|
149294
|
+
continue;
|
|
149295
|
+
}
|
|
149296
|
+
await handlers.onMessage({
|
|
149077
149297
|
messageId: String(message.message_id),
|
|
149078
149298
|
chatId: chatIdOf(message),
|
|
149079
149299
|
senderId,
|
|
@@ -149082,11 +149302,13 @@ var makeTelegramAdapter = (deps) => {
|
|
|
149082
149302
|
resources: [],
|
|
149083
149303
|
unsupportedType: unsupported
|
|
149084
149304
|
});
|
|
149305
|
+
if (!running) break;
|
|
149306
|
+
advanceOffset(update);
|
|
149085
149307
|
continue;
|
|
149086
149308
|
}
|
|
149087
149309
|
const parentChatId = parentChatIdOf(message);
|
|
149088
149310
|
const replyTo = telegramReplyReference(message, botUsername, botId);
|
|
149089
|
-
handlers.onMessage({
|
|
149311
|
+
await handlers.onMessage({
|
|
149090
149312
|
messageId: String(message.message_id),
|
|
149091
149313
|
chatId: chatIdOf(message),
|
|
149092
149314
|
...parentChatId === void 0 ? {} : { parentChatId },
|
|
@@ -149106,6 +149328,8 @@ var makeTelegramAdapter = (deps) => {
|
|
|
149106
149328
|
}
|
|
149107
149329
|
]
|
|
149108
149330
|
});
|
|
149331
|
+
if (!running) break;
|
|
149332
|
+
advanceOffset(update);
|
|
149109
149333
|
}
|
|
149110
149334
|
} catch (error) {
|
|
149111
149335
|
if (!running) return;
|
|
@@ -149581,6 +149805,7 @@ var makeTurns = (core, deps) => {
|
|
|
149581
149805
|
const startTurn = async (binding, message, accepted, inbox) => {
|
|
149582
149806
|
const chatKey = chatKeyOf(binding);
|
|
149583
149807
|
const text4 = message.text;
|
|
149808
|
+
const replayed = inbox?.replayed === true || (inbox?.entry?.attempts ?? 0) > 0;
|
|
149584
149809
|
const threadId = inbox?.entry?.threadId ?? await ensureThread(binding, message);
|
|
149585
149810
|
if (threadId === null) {
|
|
149586
149811
|
await inbox?.complete();
|
|
@@ -149685,14 +149910,10 @@ var makeTurns = (core, deps) => {
|
|
|
149685
149910
|
);
|
|
149686
149911
|
if (!dispatched) return null;
|
|
149687
149912
|
topicRoot?.confirmConsumed();
|
|
149688
|
-
|
|
149689
|
-
|
|
149690
|
-
|
|
149691
|
-
|
|
149692
|
-
userMessageId,
|
|
149693
|
-
recordedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
149694
|
-
});
|
|
149695
|
-
}
|
|
149913
|
+
await deps.conclusions.owe(
|
|
149914
|
+
{ chatKey, threadId, userMessageId, recordedAt: (/* @__PURE__ */ new Date()).toISOString() },
|
|
149915
|
+
replayed ? deps.recoverConclusion : void 0
|
|
149916
|
+
);
|
|
149696
149917
|
} finally {
|
|
149697
149918
|
registered();
|
|
149698
149919
|
}
|
|
@@ -151127,7 +151348,7 @@ ${kept}`;
|
|
|
151127
151348
|
if (message.chatType !== "single") return;
|
|
151128
151349
|
client.rememberRequest(message.chatId, message.requestId);
|
|
151129
151350
|
client.rememberConversation(message.chatId, message.conversation);
|
|
151130
|
-
handlers.onMessage({
|
|
151351
|
+
void handlers.onMessage({
|
|
151131
151352
|
// 不保存短效 req_id:重启后的回话走新连接的 aibot_send_msg 主动发送,
|
|
151132
151353
|
// 不冒充原消息的回答,也不复用旧连接的 rememberRequest / 流状态。
|
|
151133
151354
|
messageId: message.messageId,
|
|
@@ -151144,6 +151365,10 @@ ${kept}`;
|
|
|
151144
151365
|
mimeType: resource.mimeType,
|
|
151145
151366
|
kind: resource.kind
|
|
151146
151367
|
}))
|
|
151368
|
+
}).catch((error) => {
|
|
151369
|
+
deps.log.warn("wecom could not persist inbound message", {
|
|
151370
|
+
error: describeError(error)
|
|
151371
|
+
});
|
|
151147
151372
|
});
|
|
151148
151373
|
}
|
|
151149
151374
|
}
|
|
@@ -152239,12 +152464,19 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
152239
152464
|
}
|
|
152240
152465
|
const messageId = message.msgid ?? `${chatId}:${message.createTime ?? Date.now()}`;
|
|
152241
152466
|
const base2 = { messageId, chatId, senderId: chatId };
|
|
152467
|
+
const receive = (inbound) => {
|
|
152468
|
+
void handlers.onMessage(inbound).catch((error) => {
|
|
152469
|
+
deps.log.warn("wecom-app could not persist inbound message", {
|
|
152470
|
+
error: describeError(error)
|
|
152471
|
+
});
|
|
152472
|
+
});
|
|
152473
|
+
};
|
|
152242
152474
|
if (message.msgtype === "text") {
|
|
152243
|
-
|
|
152475
|
+
receive({ ...base2, text: message.text?.content?.trim() ?? "", resources: [] });
|
|
152244
152476
|
return;
|
|
152245
152477
|
}
|
|
152246
152478
|
if (message.msgtype === "image" && message.mediaId !== void 0 && message.mediaId !== "") {
|
|
152247
|
-
|
|
152479
|
+
receive({
|
|
152248
152480
|
...base2,
|
|
152249
152481
|
text: "",
|
|
152250
152482
|
resources: [
|
|
@@ -152253,7 +152485,7 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
152253
152485
|
});
|
|
152254
152486
|
return;
|
|
152255
152487
|
}
|
|
152256
|
-
|
|
152488
|
+
receive({
|
|
152257
152489
|
...base2,
|
|
152258
152490
|
text: "",
|
|
152259
152491
|
resources: [],
|
|
@@ -152463,7 +152695,7 @@ var makeWecomAppAdapter = (deps) => {
|
|
|
152463
152695
|
// src/index.ts
|
|
152464
152696
|
var definition = {
|
|
152465
152697
|
id: "im",
|
|
152466
|
-
apiVersion:
|
|
152698
|
+
apiVersion: 23,
|
|
152467
152699
|
// 第二个参数只给插件自己的测试夹具:换掉文件 IO 与派发重试的等待。宿主只传 ctx。
|
|
152468
152700
|
activate: async (ctx, testing = {}) => {
|
|
152469
152701
|
const { dispatchSleep } = testing;
|
|
@@ -152592,6 +152824,7 @@ var definition = {
|
|
|
152592
152824
|
const turns = makeTurns(core, {
|
|
152593
152825
|
conclusions,
|
|
152594
152826
|
attachments: inboundAttachments,
|
|
152827
|
+
recoverConclusion: notifications.recoverConclusion,
|
|
152595
152828
|
queueFlow: interactions.queueFlow,
|
|
152596
152829
|
rememberThreadProject: notifications.rememberThreadProject,
|
|
152597
152830
|
noteInboundUserMessage: webSync.noteInboundUserMessage,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ycodium-ai/plugin-im",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.3220",
|
|
4
4
|
"description": "Talk to your agents from Telegram, WeCom, Lark, or WeChat (experimental).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"im",
|
|
@@ -32,7 +32,8 @@
|
|
|
32
32
|
"markdown-it": "^15.0.2",
|
|
33
33
|
"react": "19.2.6",
|
|
34
34
|
"vite-plus": "0.2.2",
|
|
35
|
-
"
|
|
35
|
+
"ycodium-react-test-harness": "0.0.0",
|
|
36
|
+
"@ycodium-ai/plugin-api": "0.2.3220"
|
|
36
37
|
},
|
|
37
38
|
"ycodium": {
|
|
38
39
|
"plugin": "./dist/index.js",
|