@startsimpli/integrations 0.1.5 → 0.2.1
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/package.json +2 -2
- package/src/__tests__/corpus-copy.test.tsx +196 -0
- package/src/__tests__/corpus-extract.test.ts +537 -0
- package/src/__tests__/corpus-storage.test.ts +301 -0
- package/src/__tests__/corpus-tracker.test.tsx +250 -0
- package/src/components/Markdown.tsx +193 -0
- package/src/components/index.ts +2 -0
- package/src/components/pages/AskPage.tsx +33 -8
- package/src/components/pages/LibraryPage.tsx +10 -2
- package/src/components/pages/TrackerPage.tsx +437 -0
- package/src/corpus/client.ts +206 -18
- package/src/corpus/commitments.ts +416 -0
- package/src/corpus/entities.ts +288 -5
- package/src/corpus/extract.ts +438 -0
- package/src/corpus/fixtures.ts +602 -186
- package/src/corpus/index.ts +57 -1
- package/src/corpus/llm.ts +134 -2
- package/src/corpus/text.ts +283 -0
- package/src/corpus/types.ts +256 -0
package/src/corpus/types.ts
CHANGED
|
@@ -334,6 +334,262 @@ export interface CorpusLlm {
|
|
|
334
334
|
describe(): LlmDescription;
|
|
335
335
|
}
|
|
336
336
|
|
|
337
|
+
// ---------------------------------------------------------------------------
|
|
338
|
+
// Commitments — the one-way funnel (bd startsim-768w.38.25)
|
|
339
|
+
// ---------------------------------------------------------------------------
|
|
340
|
+
//
|
|
341
|
+
// THE PRODUCT IS THIS, and Ask is a secondary affordance over it. A meeting is
|
|
342
|
+
// conversation; what a person needs out of it is a list of things that are now
|
|
343
|
+
// owed, by whom, by when, and whether they are still owed. Once the material is
|
|
344
|
+
// structured, tracking is trivial — so all of the difficulty is deliberately
|
|
345
|
+
// concentrated on the extraction and the identity, and none of it on the list.
|
|
346
|
+
//
|
|
347
|
+
// ONE GENERIC TYPE, DISCRIMINATED BY SUBTYPE (feedback_brutally_generic, and the
|
|
348
|
+
// same rule that gave `source_document` a `subtype` instead of a `GranolaNote`
|
|
349
|
+
// model). There is no Task model, no Decision model and no Risk model. A tenant
|
|
350
|
+
// that wants a fourth kind adds a subtype string; nothing in this package, in
|
|
351
|
+
// the storage, or on the screen changes.
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* `action` — somebody owes somebody else a thing. The default and the bulk.
|
|
355
|
+
* `decision` — a thing was settled. Not owed, but the reason a later action
|
|
356
|
+
* exists, and the record people actually go looking for.
|
|
357
|
+
* `need` — a stated requirement with no owner yet. Distinct from `action`
|
|
358
|
+
* precisely because it has nobody on it; collapsing the two hides
|
|
359
|
+
* the most useful column on the screen.
|
|
360
|
+
* `risk` — something that will cost if ignored.
|
|
361
|
+
* `question` — an open question addressed to someone.
|
|
362
|
+
*
|
|
363
|
+
* A string, not a union, for the same reason `SourceKey` is: a sixth kind must
|
|
364
|
+
* not require an app change.
|
|
365
|
+
*/
|
|
366
|
+
export type CommitmentSubtype = string;
|
|
367
|
+
|
|
368
|
+
/**
|
|
369
|
+
* `open` — still owed.
|
|
370
|
+
* `done` — satisfied. Set by a human, or by RESOLUTION DETECTION against a
|
|
371
|
+
* later meeting, and in the second case `statusReason` carries the
|
|
372
|
+
* sentence that closed it and `resolvedBy` the record it came from.
|
|
373
|
+
* `blocked` — owed, but waiting on something named in `statusReason`.
|
|
374
|
+
* `dropped` — explicitly abandoned. Not the same as done and must not read as it.
|
|
375
|
+
*/
|
|
376
|
+
export type CommitmentStatus = 'open' | 'done' | 'blocked' | 'dropped';
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* One time a commitment was talked about.
|
|
380
|
+
*
|
|
381
|
+
* MENTIONS ARE THE UNIT OF IDEMPOTENCY. A commitment is identified across
|
|
382
|
+
* meetings by what it is ABOUT (see `dedupeKey`), and each meeting that
|
|
383
|
+
* discusses it contributes one mention keyed by `originKey`. Re-extracting a
|
|
384
|
+
* document REPLACES that document's mentions rather than appending, which is
|
|
385
|
+
* what makes a second run over the same corpus a no-op instead of a doubling.
|
|
386
|
+
*/
|
|
387
|
+
export interface CommitmentMention {
|
|
388
|
+
/** The `source_document` this mention came from. */
|
|
389
|
+
documentId: string;
|
|
390
|
+
/** Stable within a document: the vendor's own bullet, or the model's item. */
|
|
391
|
+
originKey: string;
|
|
392
|
+
/** When the meeting happened — NOT when the row was written. */
|
|
393
|
+
occurredAt: string | null;
|
|
394
|
+
/** Verbatim span from that document, so the claim can be checked. */
|
|
395
|
+
quote: string;
|
|
396
|
+
/** `rules` when the vendor's own structure produced it, `model` otherwise. */
|
|
397
|
+
extractor: 'rules' | 'model';
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
/** A trackable thing that came out of a conversation. */
|
|
401
|
+
export interface Commitment {
|
|
402
|
+
id: string;
|
|
403
|
+
/** `commit:<dedupeKey>` — the row's stable external identity in the tenant. */
|
|
404
|
+
externalId: string;
|
|
405
|
+
/** Identity across meetings: what it is about, plus what is being done. */
|
|
406
|
+
dedupeKey: string;
|
|
407
|
+
subtype: CommitmentSubtype;
|
|
408
|
+
/** One line, imperative. What the screen shows. */
|
|
409
|
+
title: string;
|
|
410
|
+
/** The fuller statement, when the source had one. */
|
|
411
|
+
detail: string;
|
|
412
|
+
status: CommitmentStatus;
|
|
413
|
+
/** Why it is not `open`, in the words of the record that changed it. */
|
|
414
|
+
statusReason?: string;
|
|
415
|
+
/** Which record closed or blocked it. */
|
|
416
|
+
resolvedBy?: string;
|
|
417
|
+
resolvedAt?: string | null;
|
|
418
|
+
/**
|
|
419
|
+
* Who owes it, as NAMED IN THE MEETING. A string and not a `PartyRef`,
|
|
420
|
+
* because on a real tenant almost nobody in a transcript is a Party row yet —
|
|
421
|
+
* the live tandem workspace has one Party for sixteen documents. Dropping
|
|
422
|
+
* "Megan" because Megan is not in the graph would delete the most useful
|
|
423
|
+
* column on the screen to satisfy a model that has not been populated.
|
|
424
|
+
*/
|
|
425
|
+
ownerName?: string;
|
|
426
|
+
/** Resolved to a real record when one exists. Additive, never a replacement. */
|
|
427
|
+
owner?: PartyRef;
|
|
428
|
+
/** What it is about — a matter number, a property, a vendor, a person. */
|
|
429
|
+
subject?: string;
|
|
430
|
+
/** Normalised `subject`. Half of `dedupeKey`, and the dedupe's precision. */
|
|
431
|
+
subjectKey: string;
|
|
432
|
+
/** Normalised action stems. The other half. */
|
|
433
|
+
actionKey: string;
|
|
434
|
+
/** When it is due, only when the source actually said so. Never invented. */
|
|
435
|
+
dueAt?: string | null;
|
|
436
|
+
mentions: CommitmentMention[];
|
|
437
|
+
firstSeenAt: string | null;
|
|
438
|
+
lastSeenAt: string | null;
|
|
439
|
+
/** Which extractor produced it, so a later one can re-derive without orphaning. */
|
|
440
|
+
extractorVersion: string;
|
|
441
|
+
source: SourceKey;
|
|
442
|
+
sourceLabel: string;
|
|
443
|
+
scope: CorpusScope;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
/** What the extractor proposes, before identity and merging are applied. */
|
|
447
|
+
export interface CommitmentDraft {
|
|
448
|
+
documentId: string;
|
|
449
|
+
originKey: string;
|
|
450
|
+
subtype: CommitmentSubtype;
|
|
451
|
+
title: string;
|
|
452
|
+
detail: string;
|
|
453
|
+
status: CommitmentStatus;
|
|
454
|
+
ownerName?: string;
|
|
455
|
+
subject?: string;
|
|
456
|
+
dueAt?: string | null;
|
|
457
|
+
quote: string;
|
|
458
|
+
extractor: 'rules' | 'model';
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* A statement in a LATER meeting that something previously outstanding is now
|
|
463
|
+
* settled. Extracted separately from commitments because closing an item is a
|
|
464
|
+
* different claim from creating one and must clear a higher bar — a wrong close
|
|
465
|
+
* makes the list lie in the direction of "you have nothing to do".
|
|
466
|
+
*/
|
|
467
|
+
export interface ResolutionClaim {
|
|
468
|
+
documentId: string;
|
|
469
|
+
/** What it is about, matched against `Commitment.subjectKey`. */
|
|
470
|
+
subject?: string;
|
|
471
|
+
/** What happened, matched against `Commitment.actionKey`. */
|
|
472
|
+
statement: string;
|
|
473
|
+
quote: string;
|
|
474
|
+
/** `done` or `dropped` — a thing can stop being owed without being satisfied. */
|
|
475
|
+
outcome: 'done' | 'dropped';
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
/** Server-side filters for the tracking surface (CLAUDE.md rule 8). */
|
|
479
|
+
export interface CommitmentQuery {
|
|
480
|
+
status?: CommitmentStatus | CommitmentStatus[];
|
|
481
|
+
subtype?: CommitmentSubtype;
|
|
482
|
+
owner?: string;
|
|
483
|
+
/** ISO instants over `dueAt`. */
|
|
484
|
+
dueBefore?: string;
|
|
485
|
+
/** ISO instant over `lastSeenAt` — "what changed since last week". */
|
|
486
|
+
changedAfter?: string;
|
|
487
|
+
search?: string;
|
|
488
|
+
page?: number;
|
|
489
|
+
pageSize?: number;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* The tracking surface's contract. Separate from `CorpusApi` because a
|
|
494
|
+
* deployment can have a corpus without an extractor — the Library and Ask work
|
|
495
|
+
* with no commitment rows at all, and a composer that required them would make
|
|
496
|
+
* the corpus surfaces depend on a pipeline that may not have run.
|
|
497
|
+
*/
|
|
498
|
+
export interface CommitmentApi {
|
|
499
|
+
listCommitments(query?: CommitmentQuery): Promise<CorpusPage<Commitment>>;
|
|
500
|
+
/**
|
|
501
|
+
* Persist the result of a run. ONE call carrying every row, keyed by
|
|
502
|
+
* `externalId`, so the tenant's own upsert provides the idempotency rather
|
|
503
|
+
* than a read-modify-write race in this package.
|
|
504
|
+
*/
|
|
505
|
+
saveCommitments?(commitments: Commitment[]): Promise<void>;
|
|
506
|
+
/** A human overriding the extractor. The extractor must never overwrite this. */
|
|
507
|
+
updateCommitment?(id: string, patch: Partial<Pick<Commitment,
|
|
508
|
+
'status' | 'statusReason' | 'ownerName' | 'dueAt' | 'subtype'>>): Promise<Commitment>;
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
/**
|
|
512
|
+
* The extraction seam. ONE method taking N documents, so the batching rule is
|
|
513
|
+
* structural rather than something a caller has to remember — exactly like
|
|
514
|
+
* `CorpusLlm.answer`.
|
|
515
|
+
*/
|
|
516
|
+
export interface CorpusExtractorLlm {
|
|
517
|
+
extract(input: ExtractionRequest): Promise<ExtractionDraft>;
|
|
518
|
+
describe(): LlmDescription;
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
export interface ExtractionRequest {
|
|
522
|
+
documents: Array<{
|
|
523
|
+
id: string;
|
|
524
|
+
title: string;
|
|
525
|
+
source: string;
|
|
526
|
+
occurredAt: string | null;
|
|
527
|
+
participants: string[];
|
|
528
|
+
text: string;
|
|
529
|
+
/**
|
|
530
|
+
* What the deterministic pre-pass already found in this document. Sent so
|
|
531
|
+
* the model ADDS to it rather than restating it — the vendor's own action
|
|
532
|
+
* list is not a thing worth paying a model to rediscover.
|
|
533
|
+
*/
|
|
534
|
+
alreadyFound: string[];
|
|
535
|
+
}>;
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
export interface ExtractionDraft {
|
|
539
|
+
items: Array<{
|
|
540
|
+
documentId: string;
|
|
541
|
+
subtype?: string;
|
|
542
|
+
title: string;
|
|
543
|
+
detail?: string;
|
|
544
|
+
status?: string;
|
|
545
|
+
owner?: string;
|
|
546
|
+
subject?: string;
|
|
547
|
+
dueAt?: string | null;
|
|
548
|
+
quote?: string;
|
|
549
|
+
}>;
|
|
550
|
+
resolutions?: Array<{
|
|
551
|
+
documentId: string;
|
|
552
|
+
subject?: string;
|
|
553
|
+
statement: string;
|
|
554
|
+
quote?: string;
|
|
555
|
+
outcome?: string;
|
|
556
|
+
}>;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
/**
|
|
560
|
+
* What `TrackerPage` is injected with. An app points it at its own route
|
|
561
|
+
* handler; the handler runs the extraction server-side, where the key lives.
|
|
562
|
+
*/
|
|
563
|
+
export interface CommitmentTrackerApi extends CommitmentApi {
|
|
564
|
+
/**
|
|
565
|
+
* Run extraction over documents that have not been processed. Optional: a
|
|
566
|
+
* read-only deployment omits it and the control does not render.
|
|
567
|
+
*/
|
|
568
|
+
extract?(input: { scope: CorpusScope; documentIds?: string[] }): Promise<ExtractionResult>;
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
export interface ExtractionResult {
|
|
572
|
+
/** Documents read and sent to the extractor. */
|
|
573
|
+
documentsProcessed: number;
|
|
574
|
+
/** Items the vendor's own structure produced, with no model involved. */
|
|
575
|
+
fromRules: number;
|
|
576
|
+
/** Items only the model found. */
|
|
577
|
+
fromModel: number;
|
|
578
|
+
created: number;
|
|
579
|
+
/** Existing items a new mention was attached to — the dedupe, counted. */
|
|
580
|
+
merged: number;
|
|
581
|
+
/** Items a later meeting closed. */
|
|
582
|
+
resolved: number;
|
|
583
|
+
/**
|
|
584
|
+
* Resolution claims that matched an open item too weakly to act on. Surfaced
|
|
585
|
+
* for review rather than applied: closing the wrong item makes the list lie in
|
|
586
|
+
* the one direction a to-do list must never lie.
|
|
587
|
+
*/
|
|
588
|
+
needsReview: Array<{ commitmentId: string; documentId: string; quote: string }>;
|
|
589
|
+
droppedOutOfScope: number;
|
|
590
|
+
provider: LlmDescription;
|
|
591
|
+
}
|
|
592
|
+
|
|
337
593
|
/**
|
|
338
594
|
* What `AskPage` is injected with. An app points it at its own route handler;
|
|
339
595
|
* the handler calls `askCorpus` server-side, where the API key lives.
|