@vibesdotdev/session 1.0.0-rc.4 → 1.0.0-rc.8
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/admission/admit-caller.d.ts.map +1 -1
- package/dist/admission/admit-caller.js +8 -3
- package/dist/admission/admit-caller.js.map +1 -1
- package/dist/cli/chat/session.repair-slugs.descriptor.d.ts +4 -0
- package/dist/cli/chat/session.repair-slugs.descriptor.d.ts.map +1 -0
- package/dist/cli/chat/session.repair-slugs.descriptor.js +16 -0
- package/dist/cli/chat/session.repair-slugs.descriptor.js.map +1 -0
- package/dist/cli/chat/session.repair-slugs.impl.d.ts +6 -0
- package/dist/cli/chat/session.repair-slugs.impl.d.ts.map +1 -0
- package/dist/cli/chat/session.repair-slugs.impl.js +25 -0
- package/dist/cli/chat/session.repair-slugs.impl.js.map +1 -0
- package/dist/cli/command.d.ts +1 -1
- package/dist/cli/command.d.ts.map +1 -1
- package/dist/cli/command.js.map +1 -1
- package/dist/cli/schemas/commands.d.ts +21 -0
- package/dist/cli/schemas/commands.d.ts.map +1 -1
- package/dist/cli/schemas/commands.js +10 -0
- package/dist/cli/schemas/commands.js.map +1 -1
- package/dist/cli/services/session/participants.d.ts +7 -1
- package/dist/cli/services/session/participants.d.ts.map +1 -1
- package/dist/cli/services/session/participants.js +6 -0
- package/dist/cli/services/session/participants.js.map +1 -1
- package/dist/cli/services/session.cli.consumer.d.ts +1 -1
- package/dist/cli/services/session.cli.consumer.d.ts.map +1 -1
- package/dist/cli/services/session.cli.consumer.js +1 -1
- package/dist/cli/services/session.cli.consumer.js.map +1 -1
- package/dist/cli/session-cli.plugin.d.ts.map +1 -1
- package/dist/cli/session-cli.plugin.js +3 -0
- package/dist/cli/session-cli.plugin.js.map +1 -1
- package/dist/daemon/commands/holds.d.ts +12 -0
- package/dist/daemon/commands/holds.d.ts.map +1 -0
- package/dist/daemon/commands/holds.js +27 -0
- package/dist/daemon/commands/holds.js.map +1 -0
- package/dist/docs/session.docs.descriptor.d.ts.map +1 -1
- package/dist/docs/session.docs.descriptor.js +20 -7
- package/dist/docs/session.docs.descriptor.js.map +1 -1
- package/dist/index/session-index.context.descriptor.d.ts +33 -0
- package/dist/index/session-index.context.descriptor.d.ts.map +1 -0
- package/dist/index/session-index.context.descriptor.js +11 -0
- package/dist/index/session-index.context.descriptor.js.map +1 -0
- package/dist/index/session-index.context.impl.consumer.d.ts +8 -0
- package/dist/index/session-index.context.impl.consumer.d.ts.map +1 -0
- package/dist/index/session-index.context.impl.consumer.js +7 -0
- package/dist/index/session-index.context.impl.consumer.js.map +1 -0
- package/dist/index/session-index.d.ts +29 -0
- package/dist/index/session-index.d.ts.map +1 -0
- package/dist/index/session-index.js +68 -0
- package/dist/index/session-index.js.map +1 -0
- package/dist/index/session-index.record.descriptor.d.ts +49 -0
- package/dist/index/session-index.record.descriptor.d.ts.map +1 -0
- package/dist/index/session-index.record.descriptor.js +59 -0
- package/dist/index/session-index.record.descriptor.js.map +1 -0
- package/dist/manager.d.ts +22 -1
- package/dist/manager.d.ts.map +1 -1
- package/dist/manager.js +77 -45
- package/dist/manager.js.map +1 -1
- package/dist/mcp/utils/participant.d.ts +18 -0
- package/dist/mcp/utils/participant.d.ts.map +1 -1
- package/dist/mcp/utils/participant.js +97 -6
- package/dist/mcp/utils/participant.js.map +1 -1
- package/dist/schemas/chatter.d.ts +2 -0
- package/dist/schemas/chatter.d.ts.map +1 -1
- package/dist/schemas/chatter.js +4 -0
- package/dist/schemas/chatter.js.map +1 -1
- package/dist/schemas/participant.d.ts +3 -0
- package/dist/schemas/participant.d.ts.map +1 -1
- package/dist/schemas/participant.js +10 -0
- package/dist/schemas/participant.js.map +1 -1
- package/dist/session.consumer.plugin.d.ts.map +1 -1
- package/dist/session.consumer.plugin.js +5 -1
- package/dist/session.consumer.plugin.js.map +1 -1
- package/dist/storage/session-connections.record.descriptor.d.ts +1 -0
- package/dist/storage/session-connections.record.descriptor.d.ts.map +1 -1
- package/dist/storage/session-messages.record.descriptor.d.ts +1 -0
- package/dist/storage/session-messages.record.descriptor.d.ts.map +1 -1
- package/dist/storage/write-or-recover.d.ts +21 -0
- package/dist/storage/write-or-recover.d.ts.map +1 -0
- package/dist/storage/write-or-recover.js +46 -0
- package/dist/storage/write-or-recover.js.map +1 -0
- package/dist/tools/lifecycle/init/schemas.d.ts.map +1 -1
- package/dist/tools/lifecycle/init/schemas.js +5 -2
- package/dist/tools/lifecycle/init/schemas.js.map +1 -1
- package/dist/tools/lifecycle/snapshot/handler.d.ts +1 -0
- package/dist/tools/lifecycle/snapshot/handler.d.ts.map +1 -1
- package/dist/tools/lifecycle/tool/session.tool.descriptor.d.ts.map +1 -1
- package/dist/tools/lifecycle/tool/session.tool.descriptor.js +3 -2
- package/dist/tools/lifecycle/tool/session.tool.descriptor.js.map +1 -1
- package/dist/tools/messages/handlers.d.ts.map +1 -1
- package/dist/tools/messages/handlers.js +2 -0
- package/dist/tools/messages/handlers.js.map +1 -1
- package/dist/tools/messages/tool/session-collaborate.descriptor.d.ts.map +1 -1
- package/dist/tools/messages/tool/session-collaborate.descriptor.js +3 -2
- package/dist/tools/messages/tool/session-collaborate.descriptor.js.map +1 -1
- package/dist/tools/schema-descriptions.d.ts +16 -0
- package/dist/tools/schema-descriptions.d.ts.map +1 -0
- package/dist/tools/schema-descriptions.js +94 -0
- package/dist/tools/schema-descriptions.js.map +1 -0
- package/dist/tools/scratchpad/tool/session-scratchpad.descriptor.d.ts.map +1 -1
- package/dist/tools/scratchpad/tool/session-scratchpad.descriptor.js +3 -2
- package/dist/tools/scratchpad/tool/session-scratchpad.descriptor.js.map +1 -1
- package/dist/tools/todos/list/handler.js +1 -1
- package/dist/tools/todos/list/handler.js.map +1 -1
- package/dist/tools/todos/list/schemas.d.ts +1 -0
- package/dist/tools/todos/list/schemas.d.ts.map +1 -1
- package/dist/tools/todos/list/schemas.js +5 -0
- package/dist/tools/todos/list/schemas.js.map +1 -1
- package/dist/tools/todos/tool/schemas/index.d.ts +1 -0
- package/dist/tools/todos/tool/schemas/index.d.ts.map +1 -1
- package/dist/tools/todos/tool/session-todo.descriptor.d.ts.map +1 -1
- package/dist/tools/todos/tool/session-todo.descriptor.js +3 -2
- package/dist/tools/todos/tool/session-todo.descriptor.js.map +1 -1
- package/dist/tools/todos/tool/session-todo.variant.list.impl.d.ts +1 -0
- package/dist/tools/todos/tool/session-todo.variant.list.impl.d.ts.map +1 -1
- package/dist/tools/workspace/tool/schemas/index.d.ts +8 -1
- package/dist/tools/workspace/tool/schemas/index.d.ts.map +1 -1
- package/dist/tools/workspace/tool/schemas/index.js +5 -1
- package/dist/tools/workspace/tool/schemas/index.js.map +1 -1
- package/dist/tools/workspace/tool/session-workspace.descriptor.d.ts.map +1 -1
- package/dist/tools/workspace/tool/session-workspace.descriptor.js +6 -3
- package/dist/tools/workspace/tool/session-workspace.descriptor.js.map +1 -1
- package/dist/tools/workspace/tool/session-workspace.variant.list.impl.d.ts +1 -0
- package/dist/tools/workspace/tool/session-workspace.variant.list.impl.d.ts.map +1 -1
- package/dist/tools/workspace/tool/session-workspace.variant.pair.impl.d.ts +1 -0
- package/dist/tools/workspace/tool/session-workspace.variant.pair.impl.d.ts.map +1 -1
- package/dist/tools/workspace/tool/session-workspace.variant.unpair.impl.d.ts +1 -0
- package/dist/tools/workspace/tool/session-workspace.variant.unpair.impl.d.ts.map +1 -1
- package/package.json +52 -19
package/dist/manager.js
CHANGED
|
@@ -18,7 +18,7 @@ import { getVibesClient } from '@vibesdotdev/kernel/client';
|
|
|
18
18
|
import { JsonValueSchema as JsonSchema } from '@vibesdotdev/kernel/schemas/json';
|
|
19
19
|
import { getLogger } from '@vibesdotdev/logging';
|
|
20
20
|
import { StorageRecord } from '@vibesdotdev/storage/record';
|
|
21
|
-
import {
|
|
21
|
+
import { isUniqueConstraintError, writeOrRecover } from "./storage/write-or-recover.js";
|
|
22
22
|
import { ScratchpadContentTypeSchema } from "./schemas/index.js";
|
|
23
23
|
import { HarnessSeatRegistrationSchema, HarnessSeatSchema, harnessSeatParticipantId, harnessSeatSlug } from "./schemas/harness-seat.js";
|
|
24
24
|
import { ensureSessionContextWithManager, getOrCreateMCPParticipant, resolveParticipantReference, participantSlugOf, sanitizeSlugCandidate, scanParticipantsBySlug } from "./mcp/utils/participant.js";
|
|
@@ -31,44 +31,6 @@ const SESSION_STATE_MCP_PREFIX = 'mcp:';
|
|
|
31
31
|
const PARTICIPANT_ACTIVE_WINDOW_MS = 5 * 60 * 1000;
|
|
32
32
|
/** The runtime client id notifications register their delivery client under. */
|
|
33
33
|
const NOTIFICATIONS_CLIENT_ID = 'notifications.delivery';
|
|
34
|
-
/**
|
|
35
|
-
* Whether a storage write lost a unique-index race. LocalDB's driver classifies
|
|
36
|
-
* the collision on its typed refusal (`unique-constraint`); the adapter's text
|
|
37
|
-
* is never read. A conflict another adapter reports untyped is not recognised
|
|
38
|
-
* here and propagates (cross-lane: storage has no adapter-neutral conflict code).
|
|
39
|
-
*/
|
|
40
|
-
function isUniqueConstraintError(failure) {
|
|
41
|
-
return storageFailureCondition(failure) === 'unique-constraint';
|
|
42
|
-
}
|
|
43
|
-
/**
|
|
44
|
-
* Run `write`; when it fails with a failure `recoverable` admits, run
|
|
45
|
-
* `recover` instead. Any other failure of `write`, and a recovery that finds
|
|
46
|
-
* nothing (`null`), rethrows the write's own failure; a recovery that throws
|
|
47
|
-
* carries its own failure. The write's failure is never dropped.
|
|
48
|
-
*/
|
|
49
|
-
async function writeOrRecover(write, recover, recoverable) {
|
|
50
|
-
let lost;
|
|
51
|
-
const steps = [
|
|
52
|
-
write,
|
|
53
|
-
async () => {
|
|
54
|
-
if (!recoverable(CaughtFailureSchema.parse(lost)))
|
|
55
|
-
throw lost;
|
|
56
|
-
const recovered = await recover();
|
|
57
|
-
if (recovered === null)
|
|
58
|
-
throw lost;
|
|
59
|
-
return recovered;
|
|
60
|
-
}
|
|
61
|
-
];
|
|
62
|
-
for (const step of steps) {
|
|
63
|
-
try {
|
|
64
|
-
return await step();
|
|
65
|
-
}
|
|
66
|
-
catch (error) {
|
|
67
|
-
lost = error;
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
throw lost;
|
|
71
|
-
}
|
|
72
34
|
/** Any write failure admits a recovery read (the recovery itself confirms the race). */
|
|
73
35
|
function anyFailure() {
|
|
74
36
|
return true;
|
|
@@ -1870,8 +1832,14 @@ export class SessionManager {
|
|
|
1870
1832
|
});
|
|
1871
1833
|
}
|
|
1872
1834
|
}
|
|
1873
|
-
|
|
1835
|
+
// The handle this key's own row still holds, live or lapsed: handles are
|
|
1836
|
+
// unique, so a lapsed row that still holds one holds it for no one else.
|
|
1837
|
+
const kept = existing && !existing.disconnectedAt ? existing.activeHandle ?? undefined : undefined;
|
|
1874
1838
|
const base = input.agentId ?? input.harness ?? input.clientName;
|
|
1839
|
+
const preferred = chosen ? undefined : await this.usablePreferredHandle(input, existing);
|
|
1840
|
+
// A conversation arriving under a new key (its id changed) keeps the handle
|
|
1841
|
+
// it holds under the old one: one conversation is one seat.
|
|
1842
|
+
const inherited = chosen || kept ? undefined : await this.conversationHandle(input, nowIso);
|
|
1875
1843
|
const fields = {
|
|
1876
1844
|
participantId: input.participantId ?? existing?.participantId,
|
|
1877
1845
|
clientName: input.clientName,
|
|
@@ -1883,14 +1851,23 @@ export class SessionManager {
|
|
|
1883
1851
|
agentId: input.agentId,
|
|
1884
1852
|
harness: input.harness,
|
|
1885
1853
|
harnessSessionId: input.harnessSessionId,
|
|
1854
|
+
homeWorkspace: input.homeWorkspace ?? existing?.homeWorkspace ?? null,
|
|
1886
1855
|
lastHeartbeatAt: nowAt,
|
|
1887
1856
|
expiresAt,
|
|
1888
1857
|
disconnectedAt: null
|
|
1889
1858
|
};
|
|
1859
|
+
// The handle to try, in order: the one demanded; else the one preferred (the
|
|
1860
|
+
// seat's home handle), then the one already held, then a random one.
|
|
1861
|
+
const candidates = chosen ? [] : [preferred, kept ?? inherited].filter((handle) => !!handle);
|
|
1890
1862
|
for (let attempt = 0; attempt < SessionManager.INSTANCE_HANDLE_ATTEMPTS; attempt++) {
|
|
1891
|
-
const handle = chosen ??
|
|
1892
|
-
if (handle !== existing?.activeHandle)
|
|
1893
|
-
await this.releaseExpiredHandle(handle, input.instanceId, nowIso
|
|
1863
|
+
const handle = chosen ?? candidates[attempt] ?? SessionManager.randomHandle(base);
|
|
1864
|
+
if (handle !== existing?.activeHandle) {
|
|
1865
|
+
await this.releaseExpiredHandle(handle, input.instanceId, nowIso, {
|
|
1866
|
+
participantId: fields.participantId,
|
|
1867
|
+
harnessSessionId: input.harnessSessionId,
|
|
1868
|
+
transport: input.transport
|
|
1869
|
+
});
|
|
1870
|
+
}
|
|
1894
1871
|
const row = { ...fields, handle, activeHandle: handle };
|
|
1895
1872
|
const written = await settle(() => existing
|
|
1896
1873
|
? storage.update(input.instanceId, row)
|
|
@@ -1919,8 +1896,50 @@ export class SessionManager {
|
|
|
1919
1896
|
reason: 'instance-handle-exhausted'
|
|
1920
1897
|
});
|
|
1921
1898
|
}
|
|
1922
|
-
/**
|
|
1923
|
-
async
|
|
1899
|
+
/** The handle `input`'s own conversation holds under another instance key it succeeds, if any. */
|
|
1900
|
+
async conversationHandle(input, nowIso) {
|
|
1901
|
+
if (!input.harnessSessionId || !input.participantId)
|
|
1902
|
+
return undefined;
|
|
1903
|
+
const rows = await this.connectionsStorage.list({
|
|
1904
|
+
where: { harnessSessionId: input.harnessSessionId, participantId: input.participantId },
|
|
1905
|
+
limit: 10
|
|
1906
|
+
});
|
|
1907
|
+
const sibling = rows.items.find((row) => row.connectionId !== input.instanceId &&
|
|
1908
|
+
!!row.activeHandle &&
|
|
1909
|
+
!row.disconnectedAt &&
|
|
1910
|
+
SessionManager.succeeds(row, input.transport, nowIso));
|
|
1911
|
+
return sibling?.activeHandle ?? undefined;
|
|
1912
|
+
}
|
|
1913
|
+
/**
|
|
1914
|
+
* Whether a new key of the same conversation succeeds `row`: the row has
|
|
1915
|
+
* lapsed, or it is on the same transport (a restart). A conversation live
|
|
1916
|
+
* on two transports at once (an MCP connection and an in-process host) is
|
|
1917
|
+
* two instances, and neither takes the other's handle or closes it.
|
|
1918
|
+
*/
|
|
1919
|
+
static succeeds(row, transport, nowIso) {
|
|
1920
|
+
return !SessionManager.isLiveInstance(row, nowIso) || (!!transport && row.transport === transport);
|
|
1921
|
+
}
|
|
1922
|
+
/**
|
|
1923
|
+
* The preferred handle an instance may take: sanitized, and not another
|
|
1924
|
+
* participant's name (a live handle resolves ahead of slugs, so it would
|
|
1925
|
+
* capture that participant's messages). Undefined when there is none.
|
|
1926
|
+
*/
|
|
1927
|
+
async usablePreferredHandle(input, existing) {
|
|
1928
|
+
const preferred = input.preferredHandle?.trim() ? sanitizeSlugCandidate(input.preferredHandle) : undefined;
|
|
1929
|
+
if (!preferred)
|
|
1930
|
+
return undefined;
|
|
1931
|
+
const named = (await scanParticipantsBySlug(this, preferred)).match;
|
|
1932
|
+
const actsAs = input.participantId ?? existing?.participantId;
|
|
1933
|
+
return named && named.participantId !== actsAs ? undefined : preferred;
|
|
1934
|
+
}
|
|
1935
|
+
/**
|
|
1936
|
+
* Free `handle` for `claimant` when its holder is no longer live, or when the
|
|
1937
|
+
* holder is the claimant's own conversation under another instance key the
|
|
1938
|
+
* claimant succeeds (same participant, same harness conversation; lapsed or on
|
|
1939
|
+
* the same transport): one conversation is one seat, so the stale key is
|
|
1940
|
+
* closed and the handle moves with it.
|
|
1941
|
+
*/
|
|
1942
|
+
async releaseExpiredHandle(handle, claimant, nowIso, seat = {}) {
|
|
1924
1943
|
const held = await this.connectionsStorage.list({
|
|
1925
1944
|
where: { activeHandle: handle },
|
|
1926
1945
|
limit: 1
|
|
@@ -1928,6 +1947,14 @@ export class SessionManager {
|
|
|
1928
1947
|
const holder = held.items[0];
|
|
1929
1948
|
if (!holder || holder.connectionId === claimant)
|
|
1930
1949
|
return;
|
|
1950
|
+
const sameConversation = !!seat.harnessSessionId &&
|
|
1951
|
+
holder.harnessSessionId === seat.harnessSessionId &&
|
|
1952
|
+
!!seat.participantId &&
|
|
1953
|
+
holder.participantId === seat.participantId;
|
|
1954
|
+
if (sameConversation && SessionManager.succeeds(holder, seat.transport, nowIso)) {
|
|
1955
|
+
await this.closeInstance(holder.connectionId);
|
|
1956
|
+
return;
|
|
1957
|
+
}
|
|
1931
1958
|
if (SessionManager.isLiveInstance(holder, nowIso))
|
|
1932
1959
|
return;
|
|
1933
1960
|
await this.connectionsStorage.update(holder.connectionId, { activeHandle: null });
|
|
@@ -2170,6 +2197,11 @@ export class SessionManager {
|
|
|
2170
2197
|
});
|
|
2171
2198
|
for (const message of arrived.items) {
|
|
2172
2199
|
const createdAt = message.createdAt instanceof Date ? message.createdAt : new Date(message.createdAt);
|
|
2200
|
+
// A message the sender addressed to one instance (by its handle) wakes only that instance.
|
|
2201
|
+
if (message.toInstance && message.toInstance !== instanceId) {
|
|
2202
|
+
this.deliveryCursors.set(instanceId, createdAt);
|
|
2203
|
+
continue;
|
|
2204
|
+
}
|
|
2173
2205
|
const sender = await this.getParticipant(message.fromParticipant);
|
|
2174
2206
|
await this.hostRuntime.hooks('session').emit('messageArrived', {
|
|
2175
2207
|
instanceId,
|