@semiont/make-meaning 0.5.32 → 0.5.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/dist/archivist-main.js +174 -124
- package/dist/archivist-main.js.map +1 -1
- package/dist/index.d.ts +36 -8
- package/dist/index.js +95 -49
- package/dist/index.js.map +1 -1
- package/dist/librarian-main.js +29 -10
- package/dist/librarian-main.js.map +1 -1
- package/dist/smelter-main.js +81 -45
- package/dist/smelter-main.js.map +1 -1
- package/dist/weaver-main.js +36 -13
- package/dist/weaver-main.js.map +1 -1
- package/package.json +12 -12
package/dist/index.d.ts
CHANGED
|
@@ -230,10 +230,23 @@ interface KnowledgeBase {
|
|
|
230
230
|
/** The lifecycle half of the working tree (GATEWAY.md D4a): the Archivist
|
|
231
231
|
* accessions, moves, removes and resolves — it never serves bytes. */
|
|
232
232
|
type ContentLifecycle = Pick<WorkingTreeStore, 'register' | 'move' | 'remove' | 'resolveUri'>;
|
|
233
|
-
/**
|
|
234
|
-
*
|
|
235
|
-
*
|
|
236
|
-
|
|
233
|
+
/**
|
|
234
|
+
* The record's single write seam. `Stower` is the only appendEvent caller
|
|
235
|
+
* anywhere in make-meaning or the gateway (post-#1252): single-owner by
|
|
236
|
+
* construction. A second caller is a design smell, not a wiring chore.
|
|
237
|
+
*
|
|
238
|
+
* It carries a read — `viewStorage.get`, narrowed to `get` — because one write
|
|
239
|
+
* path is at-least-once and must not duplicate the log
|
|
240
|
+
* (COMMIT-ACK-FALSE-FAILURE F3): `mark:commit` diffs its batch against what the
|
|
241
|
+
* resource already holds. This does NOT reverse JOB-RESTART-SAFETY HD1, which
|
|
242
|
+
* rejected read-before-write for a WORKER reading a REMOTE store mid-recovery
|
|
243
|
+
* — "the thing it would read is exactly what is down". This read is inside the
|
|
244
|
+
* Archivist, against the store it is about to write, and cannot be down
|
|
245
|
+
* relative to itself.
|
|
246
|
+
*/
|
|
247
|
+
type EventAppends = Pick<EventStore, 'appendEvent'> & {
|
|
248
|
+
readonly viewStorage: Pick<ViewStorage, 'get'>;
|
|
249
|
+
};
|
|
237
250
|
/** Read-only reach into the event store: the log for queries, the
|
|
238
251
|
* materializer for on-demand view assembly (`assembleResourceGraph`). */
|
|
239
252
|
interface EventStoreReads {
|
|
@@ -350,10 +363,22 @@ declare class Stower {
|
|
|
350
363
|
*
|
|
351
364
|
* Appends are sequential, not concurrent: the event log is the system of
|
|
352
365
|
* record and a batch that half-lands under concurrency is harder to reason
|
|
353
|
-
* about than one that stops at the first failure.
|
|
354
|
-
*
|
|
355
|
-
*
|
|
356
|
-
*
|
|
366
|
+
* about than one that stops at the first failure.
|
|
367
|
+
*
|
|
368
|
+
* This channel is AT-LEAST-ONCE, and the log must not grow on a repeat
|
|
369
|
+
* (COMMIT-ACK-FALSE-FAILURE F3). Two paths re-send a batch that already
|
|
370
|
+
* landed: an acknowledgement lost after a successful append (the unit is
|
|
371
|
+
* never checkpointed, so the retry re-runs exactly the unit that landed), and
|
|
372
|
+
* a partial batch, reported as a failure and retried whole. Deterministic ids
|
|
373
|
+
* (JOB-RESTART-SAFETY P3) made those safe for the PROJECTIONS — the resource
|
|
374
|
+
* view and the graph both refuse a duplicate id — but a projection's guard
|
|
375
|
+
* says nothing about the log, which appends whatever it is handed. The result
|
|
376
|
+
* was a green graph over a doubled log: silent, and not undoable.
|
|
377
|
+
*
|
|
378
|
+
* So the batch is diffed against what the resource already holds. ONE view
|
|
379
|
+
* read per commit, never per annotation: the view for a 1,673-annotation
|
|
380
|
+
* resource is ~3 MB, and re-reading it per append would cost gigabytes of
|
|
381
|
+
* parsing for a single job.
|
|
357
382
|
*/
|
|
358
383
|
private handleMarkCommit;
|
|
359
384
|
private handleMarkDelete;
|
|
@@ -1650,6 +1675,9 @@ declare class Smelter {
|
|
|
1650
1675
|
*/
|
|
1651
1676
|
private classifyEmbeddable;
|
|
1652
1677
|
/** Page through `browse:resources-requested` until the catalog is exhausted. */
|
|
1678
|
+
/** Shared with the weaver since 2026-09-09 — see `browse-resources.ts` for why the
|
|
1679
|
+
* retry lives at the page rather than around the pass. `archived: false` is
|
|
1680
|
+
* the smelter's own filter and the one difference between the two loops. */
|
|
1653
1681
|
private listAllResources;
|
|
1654
1682
|
}
|
|
1655
1683
|
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { STALL_THRESHOLD_MS, FsJobQueue } from '@semiont/jobs';
|
|
2
2
|
import { FilesystemViewStorage, resolveStorageUri, EventQuery, createEventStore } from '@semiont/event-sourcing';
|
|
3
|
-
import { getResourceEntityTypes, getResourceId, getTargetSource, resourceId, decodeRepresentation, getBodySource, getStorageUri, getPrimaryRepresentation, getTargetSelector, deriveViews, getTextPositionSelector, annotationId, errField, userId, generateUuid, getExactText, busRequest, cloneToken, assembleAnnotation, applyBodyOperations, didToAgent, isGenerationJobParams, jobId, entityType, baseUrl, busLog, BRIDGED_CHANNELS, burstBuffer, textSourceOf, chunkText, getPrimaryMediaType, yieldsGeometryOf, isAnnotatable, softwareToAgent } from '@semiont/core';
|
|
3
|
+
import { getResourceEntityTypes, getResourceId, getTargetSource, resourceId, decodeRepresentation, getBodySource, getStorageUri, getPrimaryRepresentation, getTargetSelector, deriveViews, getTextPositionSelector, annotationId, errField, userId, generateUuid, getExactText, busRequest, cloneToken, assembleAnnotation, applyBodyOperations, didToAgent, isGenerationJobParams, jobId, entityType, baseUrl, busLog, BRIDGED_CHANNELS, burstBuffer, textSourceOf, chunkText, getPrimaryMediaType, yieldsGeometryOf, isAnnotatable, retryWithBackoff, isPeerUnavailable, withDeadline, softwareToAgent } from '@semiont/core';
|
|
4
4
|
import { recordGatherDegrade, withActorSpan, recordBusEmit, withSpan, SpanKind, registerJobQueueProvider, registerVectorIndexSizeProvider } from '@semiont/observability';
|
|
5
5
|
import { createInferenceClient } from '@semiont/inference';
|
|
6
6
|
import { compareByRecencyThenId, getGraphDatabase } from '@semiont/graph';
|
|
@@ -11574,36 +11574,56 @@ var Stower = class {
|
|
|
11574
11574
|
*
|
|
11575
11575
|
* Appends are sequential, not concurrent: the event log is the system of
|
|
11576
11576
|
* record and a batch that half-lands under concurrency is harder to reason
|
|
11577
|
-
* about than one that stops at the first failure.
|
|
11578
|
-
*
|
|
11579
|
-
*
|
|
11580
|
-
*
|
|
11577
|
+
* about than one that stops at the first failure.
|
|
11578
|
+
*
|
|
11579
|
+
* This channel is AT-LEAST-ONCE, and the log must not grow on a repeat
|
|
11580
|
+
* (COMMIT-ACK-FALSE-FAILURE F3). Two paths re-send a batch that already
|
|
11581
|
+
* landed: an acknowledgement lost after a successful append (the unit is
|
|
11582
|
+
* never checkpointed, so the retry re-runs exactly the unit that landed), and
|
|
11583
|
+
* a partial batch, reported as a failure and retried whole. Deterministic ids
|
|
11584
|
+
* (JOB-RESTART-SAFETY P3) made those safe for the PROJECTIONS — the resource
|
|
11585
|
+
* view and the graph both refuse a duplicate id — but a projection's guard
|
|
11586
|
+
* says nothing about the log, which appends whatever it is handed. The result
|
|
11587
|
+
* was a green graph over a doubled log: silent, and not undoable.
|
|
11588
|
+
*
|
|
11589
|
+
* So the batch is diffed against what the resource already holds. ONE view
|
|
11590
|
+
* read per commit, never per annotation: the view for a 1,673-annotation
|
|
11591
|
+
* resource is ~3 MB, and re-reading it per append would cost gigabytes of
|
|
11592
|
+
* parsing for a single job.
|
|
11581
11593
|
*/
|
|
11582
11594
|
async handleMarkCommit(event) {
|
|
11583
11595
|
if (!event._userId) {
|
|
11584
11596
|
throw new Error("mark:commit missing _userId (gateway injection)");
|
|
11585
11597
|
}
|
|
11586
11598
|
const annotations = event.annotations ?? [];
|
|
11599
|
+
const rid = resourceId(event.resourceId);
|
|
11587
11600
|
try {
|
|
11588
|
-
|
|
11601
|
+
const view = await this.stores.eventStore.viewStorage.get(rid);
|
|
11602
|
+
const present = new Set((view?.annotations.annotations ?? []).map((a) => String(a.id)));
|
|
11589
11603
|
for (const annotation of annotations) {
|
|
11604
|
+
if (present.has(String(annotation.id))) continue;
|
|
11590
11605
|
await this.stores.eventStore.appendEvent({
|
|
11591
11606
|
type: "mark:added",
|
|
11592
|
-
resourceId:
|
|
11607
|
+
resourceId: rid,
|
|
11593
11608
|
userId: userId(event._userId),
|
|
11594
11609
|
version: 1,
|
|
11595
11610
|
payload: { annotation }
|
|
11596
11611
|
});
|
|
11597
|
-
|
|
11612
|
+
present.add(String(annotation.id));
|
|
11598
11613
|
}
|
|
11599
11614
|
this.logger.debug("Committed annotation batch", {
|
|
11600
11615
|
correlationId: event.correlationId,
|
|
11601
11616
|
resourceId: event.resourceId,
|
|
11602
|
-
persisted
|
|
11617
|
+
persisted: annotations.length
|
|
11603
11618
|
});
|
|
11604
11619
|
this.eventBus.get("mark:commit-ok").next({
|
|
11605
11620
|
correlationId: event.correlationId,
|
|
11606
|
-
|
|
11621
|
+
// The DURABLE count, which is what the acknowledgement means ("every
|
|
11622
|
+
// annotation named by the command is in the event log"). Not an append
|
|
11623
|
+
// tally: a retry whose annotations are all already present has
|
|
11624
|
+
// succeeded, and must be indistinguishable from the first commit or the
|
|
11625
|
+
// caller would have to interpret a 0 that means "all good".
|
|
11626
|
+
response: { persisted: annotations.length, annotationIds: annotations.map((a) => String(a.id)) }
|
|
11607
11627
|
});
|
|
11608
11628
|
} catch (error) {
|
|
11609
11629
|
this.logger.error("Failed to commit annotation batch", {
|
|
@@ -11829,7 +11849,11 @@ var Stower = class {
|
|
|
11829
11849
|
jobId: event.jobId,
|
|
11830
11850
|
jobType: event.jobType,
|
|
11831
11851
|
...event.annotationId ? { annotationId: event.annotationId } : {},
|
|
11832
|
-
result: event.result
|
|
11852
|
+
result: event.result,
|
|
11853
|
+
// How durability was ESTABLISHED (COMMIT-ACK-FALSE-FAILURE). An
|
|
11854
|
+
// acknowledged batch and one inferred from a probe are different
|
|
11855
|
+
// claims; absent means the question never arose.
|
|
11856
|
+
...event.durability !== void 0 ? { durability: event.durability } : {}
|
|
11833
11857
|
}
|
|
11834
11858
|
});
|
|
11835
11859
|
}
|
|
@@ -11846,7 +11870,19 @@ var Stower = class {
|
|
|
11846
11870
|
jobId: event.jobId,
|
|
11847
11871
|
jobType: event.jobType,
|
|
11848
11872
|
...event.annotationId ? { annotationId: event.annotationId } : {},
|
|
11849
|
-
error: event.error
|
|
11873
|
+
error: event.error,
|
|
11874
|
+
// The worker's JUDGMENTS, not just its message. Both are computed where
|
|
11875
|
+
// the error is still typed and are unrecoverable here — the only other
|
|
11876
|
+
// witness in the log is `error`, a flattened English string. Spread
|
|
11877
|
+
// conditionally: absent `failureClass` means UNRECOGNISED, a different
|
|
11878
|
+
// claim from 'transient', and defaulting either would write a judgment
|
|
11879
|
+
// nobody made into a log nobody can rewrite.
|
|
11880
|
+
...event.failureClass !== void 0 ? { failureClass: event.failureClass } : {},
|
|
11881
|
+
...event.willRetry !== void 0 ? { willRetry: event.willRetry } : {},
|
|
11882
|
+
// How durability was ESTABLISHED (COMMIT-ACK-FALSE-FAILURE). An
|
|
11883
|
+
// acknowledged batch and one inferred from a probe are different
|
|
11884
|
+
// claims; absent means the question never arose.
|
|
11885
|
+
...event.durability !== void 0 ? { durability: event.durability } : {}
|
|
11850
11886
|
}
|
|
11851
11887
|
});
|
|
11852
11888
|
}
|
|
@@ -13384,43 +13420,36 @@ async function createJobQueue(state, eventBus, logger) {
|
|
|
13384
13420
|
return { jobQueue, jobStatusSubscription };
|
|
13385
13421
|
}
|
|
13386
13422
|
var STARTUP_CONNECT_TIMEOUT_MS = 6e4;
|
|
13387
|
-
|
|
13388
|
-
let timer;
|
|
13389
|
-
try {
|
|
13390
|
-
return await Promise.race([
|
|
13391
|
-
work,
|
|
13392
|
-
new Promise((_resolve, reject) => {
|
|
13393
|
-
timer = setTimeout(
|
|
13394
|
-
() => reject(
|
|
13395
|
-
new Error(
|
|
13396
|
-
`${what} did not become available within ${STARTUP_CONNECT_TIMEOUT_MS / 1e3}s. Exiting so the container restart policy can retry \u2014 it is normal for a dependency to be slow when every service restarts at once.`
|
|
13397
|
-
)
|
|
13398
|
-
),
|
|
13399
|
-
STARTUP_CONNECT_TIMEOUT_MS
|
|
13400
|
-
);
|
|
13401
|
-
})
|
|
13402
|
-
]);
|
|
13403
|
-
} finally {
|
|
13404
|
-
if (timer !== void 0) clearTimeout(timer);
|
|
13405
|
-
}
|
|
13406
|
-
}
|
|
13423
|
+
var RESTART_HINT = "Exiting so the container restart policy can retry \u2014 it is normal for a dependency to be slow when every service restarts at once.";
|
|
13407
13424
|
async function connectStores(project, config, eventBus, logger, skipRebuild) {
|
|
13408
13425
|
const graphConfig = config.services.graph;
|
|
13409
13426
|
logger.info("Connecting to graph database", { type: graphConfig.type });
|
|
13410
|
-
const graphDb = await
|
|
13427
|
+
const graphDb = await withDeadline(
|
|
13428
|
+
"Graph database",
|
|
13429
|
+
STARTUP_CONNECT_TIMEOUT_MS,
|
|
13430
|
+
() => getGraphDatabase(graphConfig),
|
|
13431
|
+
RESTART_HINT
|
|
13432
|
+
);
|
|
13411
13433
|
const eventStore = createEventStore(project, eventBus, logger.child({ component: "event-store" }));
|
|
13412
13434
|
const vectorsConfig = config.services.vectors;
|
|
13413
13435
|
const embeddingConfig = config.services.embedding;
|
|
13414
13436
|
const { createVectorStore, createEmbeddingProvider } = await import('@semiont/vectors');
|
|
13415
13437
|
logger.info("Connecting to embedding provider", { type: embeddingConfig.type, model: embeddingConfig.model });
|
|
13416
|
-
const embeddingProvider = await
|
|
13438
|
+
const embeddingProvider = await withDeadline(
|
|
13417
13439
|
"Embedding provider",
|
|
13418
|
-
|
|
13440
|
+
STARTUP_CONNECT_TIMEOUT_MS,
|
|
13441
|
+
() => createEmbeddingProvider(embeddingConfig),
|
|
13442
|
+
RESTART_HINT
|
|
13419
13443
|
);
|
|
13420
13444
|
logger.info("Connecting to vector store", { type: vectorsConfig.type });
|
|
13421
|
-
const vectorStore = await
|
|
13445
|
+
const vectorStore = await withDeadline(
|
|
13422
13446
|
"Vector store",
|
|
13423
|
-
|
|
13447
|
+
STARTUP_CONNECT_TIMEOUT_MS,
|
|
13448
|
+
(signal) => createVectorStore({
|
|
13449
|
+
// The deadline this call is already being raced against, handed down so
|
|
13450
|
+
// the dimension-probe retry stops when it fires instead of being abandoned
|
|
13451
|
+
// mid-flight. This is the whole point of the signal parameter.
|
|
13452
|
+
signal,
|
|
13424
13453
|
type: vectorsConfig.type,
|
|
13425
13454
|
host: vectorsConfig.host,
|
|
13426
13455
|
port: vectorsConfig.port,
|
|
@@ -13432,7 +13461,8 @@ async function connectStores(project, config, eventBus, logger, skipRebuild) {
|
|
|
13432
13461
|
// network round-trip a precondition of booting. A `memory` store, or a
|
|
13433
13462
|
// Qdrant whose collections already exist, never consults the provider.
|
|
13434
13463
|
dimensions: () => embeddingProvider.dimensions()
|
|
13435
|
-
})
|
|
13464
|
+
}),
|
|
13465
|
+
RESTART_HINT
|
|
13436
13466
|
);
|
|
13437
13467
|
if (vectorsConfig.type === "memory") {
|
|
13438
13468
|
logger.info("memory vector store: the index rebuilds from the event log on every restart (reconcile re-embeds)");
|
|
@@ -13789,6 +13819,28 @@ function partitionByType(events) {
|
|
|
13789
13819
|
if (currentRun.length > 0) runs.push(currentRun);
|
|
13790
13820
|
return runs;
|
|
13791
13821
|
}
|
|
13822
|
+
var RESOURCE_LISTING_RETRY = {
|
|
13823
|
+
attempts: 17,
|
|
13824
|
+
initialDelayMs: 1e3,
|
|
13825
|
+
maxDelayMs: 3e4
|
|
13826
|
+
};
|
|
13827
|
+
var RESOURCES_CHANNEL = "browse:resources-requested";
|
|
13828
|
+
async function browseAllResources(bus, options) {
|
|
13829
|
+
const all = [];
|
|
13830
|
+
for (; ; ) {
|
|
13831
|
+
const page = await retryWithBackoff(
|
|
13832
|
+
() => busRequest(bus, RESOURCES_CHANNEL, {
|
|
13833
|
+
...options.archived !== void 0 ? { archived: options.archived } : {},
|
|
13834
|
+
offset: all.length,
|
|
13835
|
+
limit: options.limit
|
|
13836
|
+
}),
|
|
13837
|
+
isPeerUnavailable,
|
|
13838
|
+
RESOURCE_LISTING_RETRY
|
|
13839
|
+
);
|
|
13840
|
+
all.push(...page.resources);
|
|
13841
|
+
if (page.resources.length === 0 || all.length >= page.total) return all;
|
|
13842
|
+
}
|
|
13843
|
+
}
|
|
13792
13844
|
|
|
13793
13845
|
// src/smelter.ts
|
|
13794
13846
|
function sameStringSet(a, b) {
|
|
@@ -14544,17 +14596,11 @@ var Smelter = class _Smelter {
|
|
|
14544
14596
|
return embeddable;
|
|
14545
14597
|
}
|
|
14546
14598
|
/** Page through `browse:resources-requested` until the catalog is exhausted. */
|
|
14547
|
-
|
|
14548
|
-
|
|
14549
|
-
|
|
14550
|
-
|
|
14551
|
-
|
|
14552
|
-
"browse:resources-requested",
|
|
14553
|
-
{ archived: false, offset: all.length, limit: _Smelter.RECONCILE_PAGE_SIZE }
|
|
14554
|
-
);
|
|
14555
|
-
all.push(...page.resources);
|
|
14556
|
-
if (page.resources.length === 0 || all.length >= page.total) return all;
|
|
14557
|
-
}
|
|
14599
|
+
/** Shared with the weaver since 2026-09-09 — see `browse-resources.ts` for why the
|
|
14600
|
+
* retry lives at the page rather than around the pass. `archived: false` is
|
|
14601
|
+
* the smelter's own filter and the one difference between the two loops. */
|
|
14602
|
+
listAllResources() {
|
|
14603
|
+
return browseAllResources(this.bus, { limit: _Smelter.RECONCILE_PAGE_SIZE, archived: false });
|
|
14558
14604
|
}
|
|
14559
14605
|
};
|
|
14560
14606
|
|