@llblab/pi-kit 0.26.0 → 0.27.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/BACKLOG.md +2 -2
  2. package/CHANGELOG.md +5 -0
  3. package/README.md +4 -4
  4. package/node_modules/@llblab/pi-state-flow/AGENTS.md +1 -1
  5. package/node_modules/@llblab/pi-state-flow/BACKLOG.md +12 -5
  6. package/node_modules/@llblab/pi-state-flow/CHANGELOG.md +9 -0
  7. package/node_modules/@llblab/pi-state-flow/README.md +116 -34
  8. package/node_modules/@llblab/pi-state-flow/dist/lib/acquisition.d.ts +24 -1
  9. package/node_modules/@llblab/pi-state-flow/dist/lib/acquisition.js +80 -1
  10. package/node_modules/@llblab/pi-state-flow/dist/lib/compaction.d.ts +17 -0
  11. package/node_modules/@llblab/pi-state-flow/dist/lib/compaction.js +40 -0
  12. package/node_modules/@llblab/pi-state-flow/dist/lib/extension.js +149 -298
  13. package/node_modules/@llblab/pi-state-flow/dist/lib/git.d.ts +29 -0
  14. package/node_modules/@llblab/pi-state-flow/dist/lib/git.js +58 -0
  15. package/node_modules/@llblab/pi-state-flow/dist/lib/operation.d.ts +37 -0
  16. package/node_modules/@llblab/pi-state-flow/dist/lib/operation.js +59 -0
  17. package/node_modules/@llblab/pi-state-flow/dist/lib/ownership.d.ts +31 -0
  18. package/node_modules/@llblab/pi-state-flow/dist/lib/ownership.js +117 -0
  19. package/node_modules/@llblab/pi-state-flow/dist/lib/protocol.js +7 -7
  20. package/node_modules/@llblab/pi-state-flow/dist/lib/query.js +1 -1
  21. package/node_modules/@llblab/pi-state-flow/dist/lib/status.d.ts +2 -0
  22. package/node_modules/@llblab/pi-state-flow/dist/lib/status.js +28 -0
  23. package/node_modules/@llblab/pi-state-flow/dist/lib/transition.js +6 -1
  24. package/node_modules/@llblab/pi-state-flow/dist/package.json +1 -1
  25. package/node_modules/@llblab/pi-state-flow/dist/skills/state-flow-guide/SKILL.md +14 -6
  26. package/node_modules/@llblab/pi-state-flow/dist/skills/state-flow-memory/SKILL.md +2 -2
  27. package/node_modules/@llblab/pi-state-flow/docs/README.md +19 -9
  28. package/node_modules/@llblab/pi-state-flow/docs/agent-contract-relocation.md +4 -4
  29. package/node_modules/@llblab/pi-state-flow/docs/architecture.md +644 -91
  30. package/node_modules/@llblab/pi-state-flow/docs/compatibility.md +116 -37
  31. package/node_modules/@llblab/pi-state-flow/docs/filesystem-recovery.md +118 -21
  32. package/node_modules/@llblab/pi-state-flow/docs/fork-contract.md +68 -8
  33. package/node_modules/@llblab/pi-state-flow/docs/lazy-state.md +88 -14
  34. package/node_modules/@llblab/pi-state-flow/docs/performance.md +83 -66
  35. package/node_modules/@llblab/pi-state-flow/docs/temporal-acceptance.md +391 -62
  36. package/node_modules/@llblab/pi-state-flow/docs/usage.md +317 -62
  37. package/node_modules/@llblab/pi-state-flow/lib/acquisition.ts +85 -1
  38. package/node_modules/@llblab/pi-state-flow/lib/compaction.ts +47 -0
  39. package/node_modules/@llblab/pi-state-flow/lib/extension.ts +161 -295
  40. package/node_modules/@llblab/pi-state-flow/lib/git.ts +63 -0
  41. package/node_modules/@llblab/pi-state-flow/lib/operation.ts +75 -0
  42. package/node_modules/@llblab/pi-state-flow/lib/ownership.ts +120 -0
  43. package/node_modules/@llblab/pi-state-flow/lib/protocol.ts +7 -8
  44. package/node_modules/@llblab/pi-state-flow/lib/query.ts +1 -1
  45. package/node_modules/@llblab/pi-state-flow/lib/runtime.ts +1 -1
  46. package/node_modules/@llblab/pi-state-flow/lib/snapshot.ts +1 -2
  47. package/node_modules/@llblab/pi-state-flow/lib/status.ts +29 -0
  48. package/node_modules/@llblab/pi-state-flow/lib/transition.ts +5 -1
  49. package/node_modules/@llblab/pi-state-flow/package.json +1 -1
  50. package/node_modules/@llblab/pi-state-flow/skills/state-flow-guide/SKILL.md +14 -6
  51. package/node_modules/@llblab/pi-state-flow/skills/state-flow-memory/SKILL.md +2 -2
  52. package/node_modules/@llblab/pi-telegram/BACKLOG.md +1 -0
  53. package/node_modules/@llblab/pi-telegram/CHANGELOG.md +10 -0
  54. package/node_modules/@llblab/pi-telegram/README.md +1 -1
  55. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.d.ts +4 -1
  56. package/node_modules/@llblab/pi-telegram/dist/lib/bindings.js +16 -12
  57. package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.d.ts +1 -1
  58. package/node_modules/@llblab/pi-telegram/dist/lib/bus-follower.js +7 -1
  59. package/node_modules/@llblab/pi-telegram/dist/lib/commands.d.ts +12 -3
  60. package/node_modules/@llblab/pi-telegram/dist/lib/commands.js +137 -83
  61. package/node_modules/@llblab/pi-telegram/dist/lib/extension.js +26 -0
  62. package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.d.ts +57 -2
  63. package/node_modules/@llblab/pi-telegram/dist/lib/lifecycle.js +109 -4
  64. package/node_modules/@llblab/pi-telegram/dist/lib/model.js +2 -4
  65. package/node_modules/@llblab/pi-telegram/dist/lib/status.d.ts +3 -1
  66. package/node_modules/@llblab/pi-telegram/dist/lib/status.js +31 -1
  67. package/node_modules/@llblab/pi-telegram/dist/lib/sync.js +4 -4
  68. package/node_modules/@llblab/pi-telegram/dist/lib/threads.d.ts +1 -0
  69. package/node_modules/@llblab/pi-telegram/dist/lib/threads.js +17 -4
  70. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.d.ts +2 -2
  71. package/node_modules/@llblab/pi-telegram/dist/lib/workspace-retirement.js +48 -12
  72. package/node_modules/@llblab/pi-telegram/dist/package.json +1 -1
  73. package/node_modules/@llblab/pi-telegram/docs/architecture.md +6 -5
  74. package/node_modules/@llblab/pi-telegram/docs/multi-instance-bus.md +2 -0
  75. package/node_modules/@llblab/pi-telegram/docs/public-api.md +2 -2
  76. package/node_modules/@llblab/pi-telegram/docs/ui-style.md +4 -0
  77. package/node_modules/@llblab/pi-telegram/lib/bindings.ts +17 -10
  78. package/node_modules/@llblab/pi-telegram/lib/bus-follower.ts +8 -2
  79. package/node_modules/@llblab/pi-telegram/lib/commands.ts +135 -97
  80. package/node_modules/@llblab/pi-telegram/lib/extension.ts +25 -0
  81. package/node_modules/@llblab/pi-telegram/lib/lifecycle.ts +140 -4
  82. package/node_modules/@llblab/pi-telegram/lib/model.ts +2 -4
  83. package/node_modules/@llblab/pi-telegram/lib/status.ts +30 -1
  84. package/node_modules/@llblab/pi-telegram/lib/sync.ts +4 -4
  85. package/node_modules/@llblab/pi-telegram/lib/threads.ts +21 -3
  86. package/node_modules/@llblab/pi-telegram/lib/workspace-retirement.ts +46 -13
  87. package/node_modules/@llblab/pi-telegram/package.json +1 -1
  88. package/package.json +3 -3
@@ -229,6 +229,7 @@ export interface TelegramThreadPendingProvision {
229
229
  owner: "leader" | "manual-follower";
230
230
  instanceId: string;
231
231
  profileKey?: string;
232
+ workspaceBindingKey?: string;
232
233
  status?: "in-flight" | "ambiguous";
233
234
  threadName?: string;
234
235
  displayTitle?: string;
@@ -1528,6 +1529,9 @@ function normalizePendingProvision(
1528
1529
  ...(record.status === "in-flight" || record.status === "ambiguous"
1529
1530
  ? { status: record.status }
1530
1531
  : {}),
1532
+ ...(typeof record.workspaceBindingKey === "string"
1533
+ ? { workspaceBindingKey: record.workspaceBindingKey }
1534
+ : {}),
1531
1535
  ...(typeof record.threadName === "string"
1532
1536
  ? { threadName: record.threadName }
1533
1537
  : {}),
@@ -4919,6 +4923,13 @@ export function createTelegramTopicTargetProvisioner(
4919
4923
  pending.profileKey === request.profileKey ||
4920
4924
  pending.instanceId === request.instanceId,
4921
4925
  );
4926
+ const pendingTarget = pendingForRequest?.target;
4927
+ if (pendingForRequest && request.workspaceBindingKey &&
4928
+ pendingForRequest.workspaceBindingKey !== request.workspaceBindingKey &&
4929
+ !(pendingTarget && deps.store.listWorkspaceBindings().some((binding) =>
4930
+ binding.bindingKey === request.workspaceBindingKey && targetMatches(binding.target, pendingTarget)))) {
4931
+ throw new Error("Telegram unfinished Thread creation does not match this session binding.");
4932
+ }
4922
4933
  if (pendingForRequest?.target) {
4923
4934
  const target = pendingForRequest.target;
4924
4935
  const observation = deps.store.listSyncObservations().find((entry) =>
@@ -4933,8 +4944,13 @@ export function createTelegramTopicTargetProvisioner(
4933
4944
  pendingForRequest = undefined;
4934
4945
  }
4935
4946
  }
4947
+ const matchesWorkspace = (target: TelegramTarget): boolean =>
4948
+ !request.workspaceBindingKey ||
4949
+ deps.store.listWorkspaceBindings().some((binding) =>
4950
+ binding.bindingKey === request.workspaceBindingKey && targetMatches(binding.target, target)) ||
4951
+ (!!pendingForRequest?.target && targetMatches(pendingForRequest.target, target));
4936
4952
  const existing = deps.store.getByProfileKey(request.profileKey);
4937
- if (existing && isCurrentThreadRecord(existing)) {
4953
+ if (existing && isCurrentThreadRecord(existing) && matchesWorkspace(existing.target)) {
4938
4954
  const slot = existing.slot ?? deps.store.allocateSlot(request.profileKey);
4939
4955
  if (!slot) {
4940
4956
  throw new TelegramWorkspaceSlotUnavailableError();
@@ -5017,7 +5033,7 @@ export function createTelegramTopicTargetProvisioner(
5017
5033
  const activeForInstance = deps.store.getActiveByInstanceId(
5018
5034
  request.instanceId,
5019
5035
  );
5020
- if (activeForInstance) {
5036
+ if (activeForInstance && matchesWorkspace(activeForInstance.target)) {
5021
5037
  if (!activeForInstance.slot) {
5022
5038
  throw new Error("Telegram Workspace slot reservation is unavailable.");
5023
5039
  }
@@ -5055,7 +5071,7 @@ export function createTelegramTopicTargetProvisioner(
5055
5071
  getNextTelegramThreadNamePaletteSlot(deps.store.list(), undefined) ??
5056
5072
  request.preferredSlot;
5057
5073
  const slot =
5058
- existing?.slot ??
5074
+ (existing && matchesWorkspace(existing.target) ? existing.slot : undefined) ??
5059
5075
  deps.store.allocateSlot(
5060
5076
  request.profileKey,
5061
5077
  isManualFollowerRequest
@@ -5064,6 +5080,7 @@ export function createTelegramTopicTargetProvisioner(
5064
5080
  (candidateThreadName ? undefined : identity?.slot) ??
5065
5081
  preferredNameSlot),
5066
5082
  request.workspaceBindingKey,
5083
+ { excludeCurrentRecord: !!existing && !matchesWorkspace(existing.target) },
5067
5084
  );
5068
5085
  if (!slot) {
5069
5086
  throw new TelegramWorkspaceSlotUnavailableError();
@@ -5107,6 +5124,7 @@ export function createTelegramTopicTargetProvisioner(
5107
5124
  owner: pendingOwner,
5108
5125
  instanceId: request.instanceId,
5109
5126
  profileKey: request.profileKey,
5127
+ ...(request.workspaceBindingKey ? { workspaceBindingKey: request.workspaceBindingKey } : {}),
5110
5128
  threadName: requestThreadName,
5111
5129
  ...(displayTitle ? { displayTitle } : {}),
5112
5130
  slot,
@@ -884,6 +884,7 @@ export async function executeTelegramWorkspaceRetirement(input: {
884
884
  | "listWorkspaceRetirementIntents"
885
885
  | "commitWorkspaceRetirement"
886
886
  | "removeWorkspaceRetirementIntent"
887
+ | "markStaleByTarget"
887
888
  | "persist"
888
889
  >;
889
890
  admission: Pick<
@@ -971,6 +972,12 @@ export async function executeTelegramWorkspaceRetirement(input: {
971
972
  }
972
973
  return { kind: "retained", reason: "stale-intent" };
973
974
  }
975
+ if (fence?.phase === "commit-ready") {
976
+ // Exact confirmed absence retires any same-target routing record that
977
+ // survived the deletion attempt. The fence prevents replacement target
978
+ // publication while this stale local projection is removed.
979
+ input.store.markStaleByTarget(binding.target, "deleted");
980
+ }
974
981
  const eligible = () => input.store.captureWorkspaceSlotOccupancy(
975
982
  input.getExternalProtection,
976
983
  { expectedRetirement: input.intent },
@@ -1040,6 +1047,9 @@ export async function executeTelegramWorkspaceRetirement(input: {
1040
1047
  fence = input.admission.confirmRetirementAbsence(fence);
1041
1048
  }
1042
1049
  if (!isCurrent()) return { kind: "retained", reason: "authority-changed" };
1050
+ if (fence.phase === "commit-ready") {
1051
+ input.store.markStaleByTarget(binding.target, "deleted");
1052
+ }
1043
1053
  if (!eligible()) return { kind: "retained", reason: "protection-changed" };
1044
1054
  if (!await input.store.commitWorkspaceRetirement(input.intent, isCurrent)) {
1045
1055
  return { kind: "retained", reason: "commit-rejected" };
@@ -1212,7 +1222,7 @@ export type TelegramWorkspaceRetirementLifecycleResult =
1212
1222
  export async function runTelegramWorkspaceRetirementLifecycle(input: {
1213
1223
  store: TelegramWorkspaceRetirementPreparationDeps["store"] & Pick<
1214
1224
  TelegramTopicTargetStore,
1215
- "replaceWorkspaceRetirementIntent" | "commitWorkspaceRetirement"
1225
+ "replaceWorkspaceRetirementIntent" | "commitWorkspaceRetirement" | "markStaleByTarget"
1216
1226
  >;
1217
1227
  getExternalProtection: (
1218
1228
  binding: TelegramWorkspaceThreadBinding,
@@ -1257,19 +1267,42 @@ export async function runTelegramWorkspaceRetirementLifecycle(input: {
1257
1267
  });
1258
1268
  }
1259
1269
  if (intent) {
1260
- const adoption = await adoptTelegramWorkspaceRetirementIntent({
1261
- store: input.store,
1262
- intent,
1263
- getExternalProtection: input.getExternalProtection,
1264
- getLeaderEpoch: input.getLeaderEpoch,
1265
- getProfileKey: input.getProfileKey,
1266
- isCurrent: input.isCurrent,
1267
- runExclusive: input.runExclusive,
1268
- });
1269
- if (adoption.kind !== "adopted") {
1270
- return { kind: "blocked", stage: "adoption", reason: adoption.reason };
1270
+ if (retainedFence && isTelegramWorkspaceRetirementFence(retainedFence) &&
1271
+ retainedFence.phase === "commit-ready" &&
1272
+ matchesTelegramWorkspaceRetirementFence(retainedFence, intent)) {
1273
+ const resumed = await input.runExclusive(async () => {
1274
+ const epoch = input.getLeaderEpoch();
1275
+ const profileKey = input.getProfileKey();
1276
+ const isCurrent = () => epoch !== undefined && input.getLeaderEpoch() === epoch &&
1277
+ input.getProfileKey() === profileKey && input.isCurrent?.() !== false;
1278
+ if (!isCurrent() || intent!.profileKey !== profileKey) return undefined;
1279
+ const intents = input.store.listWorkspaceRetirementIntents();
1280
+ const binding = input.store.listWorkspaceBindings().find(candidate =>
1281
+ candidate.bindingKey === intent!.binding.bindingKey);
1282
+ if (intents.length !== 1 || !isDeepStrictEqual(intents[0], intent) ||
1283
+ !binding || !isDeepStrictEqual(binding, intent.binding)) return undefined;
1284
+ if (intent!.leaderEpoch === epoch) return intent;
1285
+ const replacement = { ...intent!, leaderEpoch: epoch! };
1286
+ return await input.store.replaceWorkspaceRetirementIntent(intent!, replacement, isCurrent)
1287
+ ? replacement : undefined;
1288
+ });
1289
+ if (!resumed) return { kind: "blocked", stage: "adoption", reason: "commit-rejected" };
1290
+ intent = resumed;
1291
+ } else {
1292
+ const adoption = await adoptTelegramWorkspaceRetirementIntent({
1293
+ store: input.store,
1294
+ intent,
1295
+ getExternalProtection: input.getExternalProtection,
1296
+ getLeaderEpoch: input.getLeaderEpoch,
1297
+ getProfileKey: input.getProfileKey,
1298
+ isCurrent: input.isCurrent,
1299
+ runExclusive: input.runExclusive,
1300
+ });
1301
+ if (adoption.kind !== "adopted") {
1302
+ return { kind: "blocked", stage: "adoption", reason: adoption.reason };
1303
+ }
1304
+ intent = adoption.intent;
1271
1305
  }
1272
- intent = adoption.intent;
1273
1306
  } else {
1274
1307
  const preparation = await prepareTelegramWorkspaceRetirement(
1275
1308
  {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llblab/pi-telegram",
3
- "version": "0.51.5",
3
+ "version": "0.51.6",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@llblab/pi-kit",
3
- "version": "0.26.0",
3
+ "version": "0.27.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -46,8 +46,8 @@
46
46
  "@llblab/pi-clean-room": "0.3.0",
47
47
  "@llblab/pi-codex-usage": "0.12.0",
48
48
  "@llblab/pi-grow-loop": "0.9.0",
49
- "@llblab/pi-state-flow": "0.24.0",
50
- "@llblab/pi-telegram": "0.51.5",
49
+ "@llblab/pi-state-flow": "0.25.0",
50
+ "@llblab/pi-telegram": "0.51.6",
51
51
  "@llblab/skills": "1.15.0"
52
52
  },
53
53
  "bundledDependencies": [