@startsimpli/integrations 0.2.16 → 0.2.17

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.
@@ -655,8 +655,14 @@ export interface ExtractionDraft {
655
655
  */
656
656
  export interface CommitmentTrackerApi extends CommitmentApi {
657
657
  /**
658
- * Run extraction over documents that have not been processed. Optional: a
659
- * read-only deployment omits it and the control does not render.
658
+ * Run ONE slice of extraction and wait for it.
659
+ *
660
+ * KEPT, but no longer what the button uses. It is the primitive a job is made
661
+ * of, and it is what a deployment small enough to finish inside a request can
662
+ * still call directly. Anything with a real corpus must use the pair below:
663
+ * one document is about twenty seconds of model time, so a seventeen-document
664
+ * sweep is six minutes and no slice size fits a browser request
665
+ * (bd startsim-768w.38.28).
660
666
  */
661
667
  extract?(input: {
662
668
  scope: CorpusScope;
@@ -665,11 +671,170 @@ export interface CommitmentTrackerApi extends CommitmentApi {
665
671
  offset?: number;
666
672
  limit?: number;
667
673
  }): Promise<ExtractionResult>;
674
+
675
+ /**
676
+ * Start — or resume — a sweep, and return WITHOUT doing the work.
677
+ *
678
+ * The composer polls `getExtractionRun` for progress. Starting must be cheap
679
+ * and must return a durable run, because the whole point is that the thing
680
+ * doing the work outlives the request that asked for it.
681
+ */
682
+ startExtraction?(input: {
683
+ scope: CorpusScope;
684
+ /** Ignore a resumable sweep and begin again from the first document. */
685
+ restart?: boolean;
686
+ }): Promise<ExtractionRun>;
687
+
688
+ /** The current sweep, from the server's own record of it. */
689
+ getExtractionRun?(): Promise<ExtractionRun | null>;
690
+ }
691
+
692
+ /**
693
+ * WHY THE MODEL HALF DID NOT RUN.
694
+ *
695
+ * This type exists because of a specific lie. `extractCommitments` deliberately
696
+ * wraps the model call so that a provider outage costs the extras and not the
697
+ * list — which is right — but the old code turned every such failure into
698
+ * `fromModel: 0` and nothing else, and `fromModel: 0` reads exactly like "the
699
+ * model found nothing to add". On the live tandem tenant that meant a week of
700
+ * rules-only runs being reported as model runs, with a blown sixty-second client
701
+ * timeout as the invisible cause. A degraded run is fine. A degraded run that
702
+ * presents itself as a complete one is not.
703
+ */
704
+ export interface ExtractionModelFailure {
705
+ /** Which model call failed — proposing items, or checking them. */
706
+ stage: 'extract' | 'verify';
707
+ message: string;
708
+ /**
709
+ * Whether the provider client's own deadline was hit, as opposed to a 401, a
710
+ * 429 or a bug. Worth separating because it is the only one the operator fixes
711
+ * by giving the run more time rather than by fixing a credential.
712
+ */
713
+ timedOut: boolean;
714
+ }
715
+
716
+ export type ExtractionRunStatus = 'queued' | 'running' | 'succeeded' | 'failed' | 'cancelled';
717
+
718
+ /** Everything a sweep has accumulated so far, summed across its slices. */
719
+ export interface ExtractionRunTotals {
720
+ documentsProcessed: number;
721
+ fromRules: number;
722
+ fromModel: number;
723
+ ungrounded: number;
724
+ unconfirmed: number;
725
+ created: number;
726
+ merged: number;
727
+ resolved: number;
728
+ droppedOutOfScope: number;
729
+ /** Slices whose model half answered. */
730
+ modelCalls: number;
731
+ /** Slices whose model half did NOT answer. The number that used to be hidden. */
732
+ modelFailures: number;
733
+ }
734
+
735
+ /** A finished sweep, kept so the screen can say what the last one did. */
736
+ export interface ExtractionRunSummary {
737
+ runId: string;
738
+ status: ExtractionRunStatus;
739
+ startedAt: string;
740
+ finishedAt: string | null;
741
+ totals: ExtractionRunTotals;
742
+ error: string | null;
743
+ }
744
+
745
+ /**
746
+ * A SWEEP, as a durable record rather than as a request.
747
+ *
748
+ * Stored in the tenant's own generic entity store, which is what makes every
749
+ * property this design owes true at once: partial progress survives the process
750
+ * that made it, a poll from any replica sees the same run, and a driver that
751
+ * dies leaves a cursor rather than a hole. Nothing about it is Tandem-specific
752
+ * or meeting-specific — it is "a long, resumable, idempotent pass over the
753
+ * corpus", which is what every foundry's connector framework eventually needs.
754
+ */
755
+ export interface ExtractionRun {
756
+ /** Identifies this SWEEP. The row it lives in is a singleton per workspace. */
757
+ runId: string;
758
+ /**
759
+ * The workspace the sweep is over.
760
+ *
761
+ * Stored, because a background driver has no request to read it from once the
762
+ * request that started it has returned — that is the whole point of the
763
+ * record. It cannot be forged: the row lives in the tenant entity store, which
764
+ * only ever returns rows in the caller's own org, and every slice re-runs
765
+ * `enforceScope` over the documents it read anyway. Adopting a stalled sweep
766
+ * overwrites it with the adopter's freshly proven scope.
767
+ */
768
+ scope: CorpusScope;
769
+ status: ExtractionRunStatus;
770
+ /**
771
+ * The next document offset to process. THE resumption mechanism: a sweep
772
+ * interrupted at nine of seventeen restarts at ten, not at one.
773
+ */
774
+ cursor: number;
775
+ /** In-scope documents at the last slice, so progress is a fraction of something. */
776
+ totalDocuments: number;
777
+ totals: ExtractionRunTotals;
778
+ /**
779
+ * Documents this sweep has already handed to the extractor, bounded.
780
+ *
781
+ * The cursor is an offset into a list whose order can shift when a document
782
+ * arrives mid-sweep. Recording the ids is what lets the run notice that it
783
+ * finished with fewer documents read than the corpus holds and SAY so, rather
784
+ * than reporting a complete sweep that quietly skipped one.
785
+ */
786
+ processedIds: string[];
787
+ /** Documents that got the deterministic pass only, because the model did not answer. */
788
+ modelIncompleteIds: string[];
789
+ needsReview: Array<{ commitmentId: string; documentId: string; quote: string }>;
790
+ provider: LlmDescription;
791
+ startedAt: string;
792
+ updatedAt: string;
793
+ finishedAt: string | null;
794
+ /**
795
+ * WHO IS DRIVING, AND UNTIL WHEN.
796
+ *
797
+ * Not an optimisation. Two drivers over one cursor means two model calls over
798
+ * the same document, and two model calls over one document produce two
799
+ * differently-worded items that identity resolution cannot see are the same —
800
+ * which is precisely how a live re-run once turned 82 commitments into 98.
801
+ */
802
+ leaseOwner: string | null;
803
+ leaseUntil: string | null;
804
+ /** Why the sweep stopped, when it stopped badly. */
805
+ error: string | null;
806
+ lastModelFailure: ExtractionModelFailure | null;
807
+ /** Previous sweeps, newest first, bounded. */
808
+ history: ExtractionRunSummary[];
809
+ /** True when this start adopted a sweep whose driver had gone away. Transient. */
810
+ adopted?: boolean;
811
+ }
812
+
813
+ /**
814
+ * Where a run record lives. ONE ROW PER WORKSPACE, rewritten in place.
815
+ *
816
+ * A row per sweep would be more history, and it would also be unbounded growth
817
+ * in the tenant's own corpus plus a scan on every poll — the tenant entity API
818
+ * filters on `?type=` and nothing else, so "the newest run" cannot be asked for
819
+ * directly. A singleton keyed on a fixed `external_id` is one deterministic
820
+ * read, never grows, and keeps the last few sweeps inline where the screen
821
+ * actually wants them.
822
+ */
823
+ export interface ExtractionRunStore {
824
+ readRun(): Promise<ExtractionRun | null>;
825
+ writeRun(run: ExtractionRun): Promise<ExtractionRun>;
668
826
  }
669
827
 
670
828
  export interface ExtractionResult {
671
829
  /** Documents read and sent to the extractor IN THIS CALL. */
672
830
  documentsProcessed: number;
831
+ /**
832
+ * Which ones, by id. A sweep needs this to record what it has really covered
833
+ * — an offset cursor alone cannot tell a resumed run whether the list moved
834
+ * under it — and the screen needs it to name the meetings a degraded slice
835
+ * only half-read.
836
+ */
837
+ documentIds: string[];
673
838
  /** Documents in scope altogether, so a partial sweep can say what it is of. */
674
839
  totalDocuments: number;
675
840
  /**
@@ -712,6 +877,15 @@ export interface ExtractionResult {
712
877
  needsReview: Array<{ commitmentId: string; documentId: string; quote: string }>;
713
878
  droppedOutOfScope: number;
714
879
  provider: LlmDescription;
880
+ /**
881
+ * `null` when the model half ran. Populated when it did not, WITH THE REASON.
882
+ *
883
+ * Read this before reading `fromModel`. Zero findings and a failed call are
884
+ * different facts and only one of them is about the corpus; conflating them is
885
+ * what let a rules-only run be reported as a model run for a week
886
+ * (bd startsim-768w.38.28).
887
+ */
888
+ modelFailure: ExtractionModelFailure | null;
715
889
  }
716
890
 
717
891
  /**