@dsh-enhanced/assistant-delivery 0.1.65 → 0.1.67
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/README.md +128 -4
- package/lib/agent-runtime.d.ts +6 -2
- package/lib/agent-runtime.d.ts.map +1 -1
- package/lib/agent-runtime.js +60 -14
- package/lib/agent-runtime.js.map +1 -1
- package/lib/attention-audit-store.d.ts +54 -0
- package/lib/attention-audit-store.d.ts.map +1 -0
- package/lib/attention-audit-store.js +213 -0
- package/lib/attention-audit-store.js.map +1 -0
- package/lib/attention-enforcement.d.ts +75 -0
- package/lib/attention-enforcement.d.ts.map +1 -0
- package/lib/attention-enforcement.js +84 -0
- package/lib/attention-enforcement.js.map +1 -0
- package/lib/attention.d.ts +203 -0
- package/lib/attention.d.ts.map +1 -0
- package/lib/attention.js +288 -0
- package/lib/attention.js.map +1 -0
- package/lib/coordinator.d.ts.map +1 -1
- package/lib/coordinator.js +6 -0
- package/lib/coordinator.js.map +1 -1
- package/lib/feedback-command.d.ts +23 -0
- package/lib/feedback-command.d.ts.map +1 -1
- package/lib/feedback-command.js +176 -0
- package/lib/feedback-command.js.map +1 -1
- package/lib/foreground-observation.d.ts +21 -0
- package/lib/foreground-observation.d.ts.map +1 -1
- package/lib/foreground-observation.js.map +1 -1
- package/lib/foreground-termination.d.ts +31 -0
- package/lib/foreground-termination.d.ts.map +1 -0
- package/lib/foreground-termination.js +42 -0
- package/lib/foreground-termination.js.map +1 -0
- package/lib/in-process-reload-runtime.d.ts +87 -0
- package/lib/in-process-reload-runtime.d.ts.map +1 -0
- package/lib/in-process-reload-runtime.js +319 -0
- package/lib/in-process-reload-runtime.js.map +1 -0
- package/lib/index.d.ts +20 -6
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +9 -3
- package/lib/index.js.map +1 -1
- package/lib/native-termination.d.ts +12 -0
- package/lib/native-termination.d.ts.map +1 -0
- package/lib/native-termination.js +57 -0
- package/lib/native-termination.js.map +1 -0
- package/lib/native-web-owner.d.ts +7 -0
- package/lib/native-web-owner.d.ts.map +1 -1
- package/lib/native-web-owner.js +40 -10
- package/lib/native-web-owner.js.map +1 -1
- package/lib/operator-snapshot.d.ts +31 -3
- package/lib/operator-snapshot.d.ts.map +1 -1
- package/lib/operator-snapshot.js +120 -47
- package/lib/operator-snapshot.js.map +1 -1
- package/lib/progress-attention.d.ts +55 -0
- package/lib/progress-attention.d.ts.map +1 -0
- package/lib/progress-attention.js +128 -0
- package/lib/progress-attention.js.map +1 -0
- package/lib/service.d.ts +515 -4
- package/lib/service.d.ts.map +1 -1
- package/lib/service.js +2183 -130
- package/lib/service.js.map +1 -1
- package/lib/sqlite.d.ts +1 -1
- package/lib/sqlite.d.ts.map +1 -1
- package/lib/sqlite.js +102 -2
- package/lib/sqlite.js.map +1 -1
- package/lib/store.d.ts +113 -2
- package/lib/store.d.ts.map +1 -1
- package/lib/store.js +450 -39
- package/lib/store.js.map +1 -1
- package/lib/types.d.ts +175 -0
- package/lib/types.d.ts.map +1 -1
- package/lib/types.js.map +1 -1
- package/lib/version.d.ts +1 -1
- package/lib/version.js +1 -1
- package/lib/work-item-engineering-context.d.ts +127 -0
- package/lib/work-item-engineering-context.d.ts.map +1 -0
- package/lib/work-item-engineering-context.js +151 -0
- package/lib/work-item-engineering-context.js.map +1 -0
- package/lib/work-item-feedback-schema.d.ts +2 -0
- package/lib/work-item-feedback-schema.d.ts.map +1 -0
- package/lib/work-item-feedback-schema.js +29 -0
- package/lib/work-item-feedback-schema.js.map +1 -0
- package/lib/work-item-feedback-store.d.ts +29 -0
- package/lib/work-item-feedback-store.d.ts.map +1 -0
- package/lib/work-item-feedback-store.js +111 -0
- package/lib/work-item-feedback-store.js.map +1 -0
- package/lib/work-item-feedback.d.ts +157 -0
- package/lib/work-item-feedback.d.ts.map +1 -0
- package/lib/work-item-feedback.js +2 -0
- package/lib/work-item-feedback.js.map +1 -0
- package/lib/work-item-replies-schema.d.ts +3 -0
- package/lib/work-item-replies-schema.d.ts.map +1 -0
- package/lib/work-item-replies-schema.js +45 -0
- package/lib/work-item-replies-schema.js.map +1 -0
- package/lib/work-item-replies-store.d.ts +43 -0
- package/lib/work-item-replies-store.d.ts.map +1 -0
- package/lib/work-item-replies-store.js +203 -0
- package/lib/work-item-replies-store.js.map +1 -0
- package/lib/work-item-replies.d.ts +93 -0
- package/lib/work-item-replies.d.ts.map +1 -0
- package/lib/work-item-replies.js +15 -0
- package/lib/work-item-replies.js.map +1 -0
- package/package.json +24 -24
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import type { DatabaseSync } from 'node:sqlite';
|
|
2
|
+
import { type AttentionAssessment, type AttentionAuditEntry, type AttentionLane, type AttentionLedgerView, type NotificationOutcomeFact } from './attention.js';
|
|
3
|
+
/** Append-only WP19 stage-1 audit. One row per Outbox record; replays keep the first verdict. */
|
|
4
|
+
export declare const attentionAuditSchema = "\n CREATE TABLE delivery_attention_audit (\n outbox_id TEXT PRIMARY KEY REFERENCES outbox_messages(id),\n audit_sequence INTEGER NOT NULL UNIQUE CHECK(audit_sequence>=1),\n binding_id TEXT NOT NULL,\n lane TEXT NOT NULL CHECK(lane IN ('background','background-route','owner-notification','automation-result','work-item-report','scheduled-goal-result')),\n source_id TEXT NOT NULL,\n purpose TEXT NOT NULL CHECK(purpose IN ('result','attention','answer')),\n purpose_source TEXT NOT NULL CHECK(purpose_source IN ('declared','lane','default')),\n work_item_id TEXT,\n question_id TEXT,\n answers_question_id TEXT,\n word_count INTEGER NOT NULL CHECK(word_count>=0),\n word_cap INTEGER NOT NULL CHECK(word_cap>=1),\n verdict TEXT NOT NULL CHECK(verdict IN ('would-accept','would-attach','would-reject')),\n reasons_json TEXT NOT NULL CHECK(json_valid(reasons_json)),\n recorded_at INTEGER NOT NULL CHECK(recorded_at>=0)\n ) STRICT;\n CREATE INDEX delivery_attention_audit_question ON delivery_attention_audit(binding_id,question_id);\n CREATE INDEX delivery_attention_audit_answer ON delivery_attention_audit(binding_id,answers_question_id);\n CREATE INDEX delivery_attention_audit_work_item ON delivery_attention_audit(binding_id,work_item_id);\n CREATE TRIGGER delivery_attention_audit_immutable BEFORE UPDATE ON delivery_attention_audit\n BEGIN SELECT RAISE(ABORT,'attention audit is append-only'); END;\n CREATE TRIGGER delivery_attention_audit_no_delete BEFORE DELETE ON delivery_attention_audit\n BEGIN SELECT RAISE(ABORT,'attention audit is durable'); END;\n";
|
|
5
|
+
export declare class AttentionAuditStore {
|
|
6
|
+
private readonly database;
|
|
7
|
+
private readonly transaction;
|
|
8
|
+
private readonly now;
|
|
9
|
+
constructor(database: DatabaseSync, transaction: <T>(fn: () => T) => T, now: () => number);
|
|
10
|
+
get(outboxId: string): AttentionAuditEntry | undefined;
|
|
11
|
+
/** Only non-rejected rows establish question state; a would-reject notice never opens or settles a question. */
|
|
12
|
+
ledger(bindingId: string): AttentionLedgerView;
|
|
13
|
+
/**
|
|
14
|
+
* Durable gap detector, derived by query (no extra state): Outbox rows that
|
|
15
|
+
* carry a durable marker of an audited lane, were created at or after the
|
|
16
|
+
* first audit row, and have no audit row. Plain `enqueueBackground` rows
|
|
17
|
+
* carry no lane marker and are not counted here. Gaps before the first
|
|
18
|
+
* successful audit write are not detectable.
|
|
19
|
+
*/
|
|
20
|
+
unauditedOutboxCount(): number;
|
|
21
|
+
/**
|
|
22
|
+
* WP19 stage 2b split-evasion input: inline bodies (Outbox text, never the
|
|
23
|
+
* attachment) of audited notices from one source on one binding and lane,
|
|
24
|
+
* recorded within the last `windowMs` of this store's clock (the same clock
|
|
25
|
+
* that stamps `recorded_at`, so the window survives restarts), in audit
|
|
26
|
+
* order. Notices that were never audited (best-effort audit write failed)
|
|
27
|
+
* are not visible here.
|
|
28
|
+
*/
|
|
29
|
+
recentInlineTexts(input: Readonly<{
|
|
30
|
+
bindingId: string;
|
|
31
|
+
lane: AttentionLane;
|
|
32
|
+
sourceId: string;
|
|
33
|
+
windowMs: number;
|
|
34
|
+
}>): readonly string[];
|
|
35
|
+
/** Assess against the committed ledger and append in one writer transaction; an existing row wins. */
|
|
36
|
+
record(input: Readonly<{
|
|
37
|
+
outboxId: string;
|
|
38
|
+
bindingId: string;
|
|
39
|
+
lane: AttentionLane;
|
|
40
|
+
sourceId: string;
|
|
41
|
+
}>, assess: (ledger: AttentionLedgerView) => AttentionAssessment): AttentionAuditEntry;
|
|
42
|
+
list(afterSequence: number, limit: number): {
|
|
43
|
+
entries: readonly AttentionAuditEntry[];
|
|
44
|
+
hasMore: boolean;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Current facts for exact notifications; unknown or unaudited ids are omitted.
|
|
48
|
+
* The unindexed JSON lookups (reply reactions, work-item feedback) run once
|
|
49
|
+
* per call over the whole batch rather than once per notification.
|
|
50
|
+
*/
|
|
51
|
+
outcomeFactsFor(outboxIds: readonly string[]): readonly NotificationOutcomeFact[];
|
|
52
|
+
private fact;
|
|
53
|
+
}
|
|
54
|
+
//# sourceMappingURL=attention-audit-store.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attention-audit-store.d.ts","sourceRoot":"","sources":["../src/attention-audit-store.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC/C,OAAO,EAAyB,KAAK,mBAAmB,EAAE,KAAK,mBAAmB,EAA6B,KAAK,aAAa,EAAE,KAAK,mBAAmB,EAAE,KAAK,uBAAuB,EAAqD,MAAM,gBAAgB,CAAA;AAEpQ,iGAAiG;AACjG,eAAO,MAAM,oBAAoB,ylDAyBhC,CAAA;AAyBD,qBAAa,mBAAmB;IAClB,OAAO,CAAC,QAAQ,CAAC,QAAQ;IAAgB,OAAO,CAAC,QAAQ,CAAC,WAAW;IAAyB,OAAO,CAAC,QAAQ,CAAC,GAAG;gBAAjG,QAAQ,EAAE,YAAY,EAAmB,WAAW,EAAE,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,KAAK,CAAC,EAAmB,GAAG,EAAE,MAAM,MAAM;IAE5I,GAAG,CAAC,QAAQ,EAAE,MAAM,GAAG,mBAAmB,GAAG,SAAS;IAKtD,gHAAgH;IAChH,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,mBAAmB;IAiB9C;;;;;;OAMG;IACH,oBAAoB,IAAI,MAAM;IAW9B;;;;;;;OAOG;IACH,iBAAiB,CAAC,KAAK,EAAE,QAAQ,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,aAAa,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,GAAG,SAAS,MAAM,EAAE;IAQrI,sGAAsG;IACtG,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,aAAa,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC,EACpG,MAAM,EAAE,CAAC,MAAM,EAAE,mBAAmB,KAAK,mBAAmB,GAAG,mBAAmB;IAepF,IAAI,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG;QAAE,OAAO,EAAE,SAAS,mBAAmB,EAAE,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE;IAMzG;;;;OAIG;IACH,eAAe,CAAC,SAAS,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,uBAAuB,EAAE;IA+BjF,OAAO,CAAC,IAAI;CAmDb"}
|
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { attentionAuditReasons, linkedReportKeyPrefix, linkedReportMetadataPrefix } from './attention.js';
|
|
2
|
+
/** Append-only WP19 stage-1 audit. One row per Outbox record; replays keep the first verdict. */
|
|
3
|
+
export const attentionAuditSchema = `
|
|
4
|
+
CREATE TABLE delivery_attention_audit (
|
|
5
|
+
outbox_id TEXT PRIMARY KEY REFERENCES outbox_messages(id),
|
|
6
|
+
audit_sequence INTEGER NOT NULL UNIQUE CHECK(audit_sequence>=1),
|
|
7
|
+
binding_id TEXT NOT NULL,
|
|
8
|
+
lane TEXT NOT NULL CHECK(lane IN ('background','background-route','owner-notification','automation-result','work-item-report','scheduled-goal-result')),
|
|
9
|
+
source_id TEXT NOT NULL,
|
|
10
|
+
purpose TEXT NOT NULL CHECK(purpose IN ('result','attention','answer')),
|
|
11
|
+
purpose_source TEXT NOT NULL CHECK(purpose_source IN ('declared','lane','default')),
|
|
12
|
+
work_item_id TEXT,
|
|
13
|
+
question_id TEXT,
|
|
14
|
+
answers_question_id TEXT,
|
|
15
|
+
word_count INTEGER NOT NULL CHECK(word_count>=0),
|
|
16
|
+
word_cap INTEGER NOT NULL CHECK(word_cap>=1),
|
|
17
|
+
verdict TEXT NOT NULL CHECK(verdict IN ('would-accept','would-attach','would-reject')),
|
|
18
|
+
reasons_json TEXT NOT NULL CHECK(json_valid(reasons_json)),
|
|
19
|
+
recorded_at INTEGER NOT NULL CHECK(recorded_at>=0)
|
|
20
|
+
) STRICT;
|
|
21
|
+
CREATE INDEX delivery_attention_audit_question ON delivery_attention_audit(binding_id,question_id);
|
|
22
|
+
CREATE INDEX delivery_attention_audit_answer ON delivery_attention_audit(binding_id,answers_question_id);
|
|
23
|
+
CREATE INDEX delivery_attention_audit_work_item ON delivery_attention_audit(binding_id,work_item_id);
|
|
24
|
+
CREATE TRIGGER delivery_attention_audit_immutable BEFORE UPDATE ON delivery_attention_audit
|
|
25
|
+
BEGIN SELECT RAISE(ABORT,'attention audit is append-only'); END;
|
|
26
|
+
CREATE TRIGGER delivery_attention_audit_no_delete BEFORE DELETE ON delivery_attention_audit
|
|
27
|
+
BEGIN SELECT RAISE(ABORT,'attention audit is durable'); END;
|
|
28
|
+
`;
|
|
29
|
+
function entry(row) {
|
|
30
|
+
const reasons = JSON.parse(row.reasons_json);
|
|
31
|
+
if (!Array.isArray(reasons) || !reasons.every(reason => attentionAuditReasons.includes(reason))) {
|
|
32
|
+
throw new Error('invalid attention audit reasons');
|
|
33
|
+
}
|
|
34
|
+
return Object.freeze({
|
|
35
|
+
sequence: row.audit_sequence, outboxId: row.outbox_id, bindingId: row.binding_id, lane: row.lane, sourceId: row.source_id,
|
|
36
|
+
purpose: row.purpose, purposeSource: row.purpose_source,
|
|
37
|
+
...(row.work_item_id === null ? {} : { workItemId: row.work_item_id }),
|
|
38
|
+
...(row.question_id === null ? {} : { questionId: row.question_id }),
|
|
39
|
+
...(row.answers_question_id === null ? {} : { answersQuestionId: row.answers_question_id }),
|
|
40
|
+
wordCount: row.word_count, wordCap: row.word_cap, verdict: row.verdict,
|
|
41
|
+
reasons: Object.freeze(reasons), recordedAt: row.recorded_at,
|
|
42
|
+
});
|
|
43
|
+
}
|
|
44
|
+
export class AttentionAuditStore {
|
|
45
|
+
database;
|
|
46
|
+
transaction;
|
|
47
|
+
now;
|
|
48
|
+
constructor(database, transaction, now) {
|
|
49
|
+
this.database = database;
|
|
50
|
+
this.transaction = transaction;
|
|
51
|
+
this.now = now;
|
|
52
|
+
}
|
|
53
|
+
get(outboxId) {
|
|
54
|
+
const row = this.database.prepare('SELECT * FROM delivery_attention_audit WHERE outbox_id=?').get(outboxId);
|
|
55
|
+
return row === undefined ? undefined : entry(row);
|
|
56
|
+
}
|
|
57
|
+
/** Only non-rejected rows establish question state; a would-reject notice never opens or settles a question. */
|
|
58
|
+
ledger(bindingId) {
|
|
59
|
+
return {
|
|
60
|
+
questionAsked: questionId => this.database.prepare(`SELECT 1 FROM delivery_attention_audit WHERE binding_id=? AND question_id=?
|
|
61
|
+
AND purpose='attention' AND verdict!='would-reject' LIMIT 1`).get(bindingId, questionId) !== undefined,
|
|
62
|
+
questionAnswered: questionId => this.database.prepare(`SELECT 1 FROM delivery_attention_audit WHERE binding_id=? AND answers_question_id=?
|
|
63
|
+
AND purpose='answer' AND verdict!='would-reject' LIMIT 1`).get(bindingId, questionId) !== undefined,
|
|
64
|
+
openQuestions: workItemId => this.database.prepare(`SELECT DISTINCT q.question_id AS id FROM delivery_attention_audit q
|
|
65
|
+
WHERE q.binding_id=? AND q.work_item_id=? AND q.purpose='attention' AND q.verdict!='would-reject' AND q.question_id IS NOT NULL
|
|
66
|
+
AND NOT EXISTS(SELECT 1 FROM delivery_attention_audit a WHERE a.binding_id=q.binding_id AND a.answers_question_id=q.question_id
|
|
67
|
+
AND a.purpose='answer' AND a.verdict!='would-reject') ORDER BY q.question_id`).all(bindingId, workItemId)
|
|
68
|
+
.map(row => row.id),
|
|
69
|
+
questionWorkItem: questionId => this.database.prepare(`SELECT work_item_id FROM delivery_attention_audit WHERE binding_id=? AND question_id=?
|
|
70
|
+
AND purpose='attention' AND verdict!='would-reject' AND work_item_id IS NOT NULL ORDER BY audit_sequence LIMIT 1`)
|
|
71
|
+
.get(bindingId, questionId)?.work_item_id,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Durable gap detector, derived by query (no extra state): Outbox rows that
|
|
76
|
+
* carry a durable marker of an audited lane, were created at or after the
|
|
77
|
+
* first audit row, and have no audit row. Plain `enqueueBackground` rows
|
|
78
|
+
* carry no lane marker and are not counted here. Gaps before the first
|
|
79
|
+
* successful audit write are not detectable.
|
|
80
|
+
*/
|
|
81
|
+
unauditedOutboxCount() {
|
|
82
|
+
return this.database.prepare(`SELECT count(*) AS count FROM outbox_messages o
|
|
83
|
+
WHERE o.created_at >= (SELECT min(recorded_at) FROM delivery_attention_audit)
|
|
84
|
+
AND NOT EXISTS(SELECT 1 FROM delivery_attention_audit a WHERE a.outbox_id=o.id)
|
|
85
|
+
AND (json_extract(o.intent_json,'$.metadata."dsh.native-notice"')='v1'
|
|
86
|
+
OR json_extract(o.intent_json,'$.metadata."dsh.route.authority"') IS NOT NULL
|
|
87
|
+
OR json_extract(o.intent_json,'$.metadata."dsh.learning.kind"') IN ('automation-run','goal-outcome')
|
|
88
|
+
OR EXISTS(SELECT 1 FROM delivery_goal_outcome_targets g WHERE g.outbox_id=o.id)
|
|
89
|
+
OR EXISTS(SELECT 1 FROM delivery_work_item_reports r WHERE r.outbox_id=o.id))`).get().count;
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* WP19 stage 2b split-evasion input: inline bodies (Outbox text, never the
|
|
93
|
+
* attachment) of audited notices from one source on one binding and lane,
|
|
94
|
+
* recorded within the last `windowMs` of this store's clock (the same clock
|
|
95
|
+
* that stamps `recorded_at`, so the window survives restarts), in audit
|
|
96
|
+
* order. Notices that were never audited (best-effort audit write failed)
|
|
97
|
+
* are not visible here.
|
|
98
|
+
*/
|
|
99
|
+
recentInlineTexts(input) {
|
|
100
|
+
return this.database.prepare(`SELECT json_extract(o.intent_json,'$.text') AS text FROM delivery_attention_audit a
|
|
101
|
+
JOIN outbox_messages o ON o.id=a.outbox_id
|
|
102
|
+
WHERE a.binding_id=? AND a.lane=? AND a.source_id=? AND a.recorded_at>=? ORDER BY a.audit_sequence`)
|
|
103
|
+
.all(input.bindingId, input.lane, input.sourceId, Math.max(0, this.now() - input.windowMs))
|
|
104
|
+
.map(row => typeof row.text === 'string' ? row.text : '');
|
|
105
|
+
}
|
|
106
|
+
/** Assess against the committed ledger and append in one writer transaction; an existing row wins. */
|
|
107
|
+
record(input, assess) {
|
|
108
|
+
return this.transaction(() => {
|
|
109
|
+
const existing = this.get(input.outboxId);
|
|
110
|
+
if (existing !== undefined)
|
|
111
|
+
return existing;
|
|
112
|
+
const assessment = assess(this.ledger(input.bindingId));
|
|
113
|
+
const next = this.database.prepare('SELECT coalesce(max(audit_sequence),0)+1 AS next FROM delivery_attention_audit').get().next;
|
|
114
|
+
this.database.prepare(`INSERT INTO delivery_attention_audit(outbox_id,audit_sequence,binding_id,lane,source_id,purpose,purpose_source,
|
|
115
|
+
work_item_id,question_id,answers_question_id,word_count,word_cap,verdict,reasons_json,recorded_at) VALUES(?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`)
|
|
116
|
+
.run(input.outboxId, next, input.bindingId, input.lane, input.sourceId, assessment.purpose, assessment.purposeSource, assessment.workItemId ?? null, assessment.questionId ?? null, assessment.answersQuestionId ?? null, assessment.wordCount, assessment.wordCap, assessment.verdict, JSON.stringify(assessment.reasons), this.now());
|
|
117
|
+
return this.get(input.outboxId);
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
list(afterSequence, limit) {
|
|
121
|
+
const rows = this.database.prepare('SELECT * FROM delivery_attention_audit WHERE audit_sequence>? ORDER BY audit_sequence LIMIT ?')
|
|
122
|
+
.all(afterSequence, limit + 1);
|
|
123
|
+
return { entries: Object.freeze(rows.slice(0, limit).map(entry)), hasMore: rows.length > limit };
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Current facts for exact notifications; unknown or unaudited ids are omitted.
|
|
127
|
+
* The unindexed JSON lookups (reply reactions, work-item feedback) run once
|
|
128
|
+
* per call over the whole batch rather than once per notification.
|
|
129
|
+
*/
|
|
130
|
+
outcomeFactsFor(outboxIds) {
|
|
131
|
+
const audits = outboxIds.map(id => this.get(id)).filter(item => item !== undefined);
|
|
132
|
+
if (audits.length === 0)
|
|
133
|
+
return Object.freeze([]);
|
|
134
|
+
const ids = JSON.stringify(audits.map(audit => audit.outboxId));
|
|
135
|
+
// A reply counts when it answers the provider message in the same
|
|
136
|
+
// conversation from the same principal, including after a new-session
|
|
137
|
+
// command moved the conversation to a newer binding generation.
|
|
138
|
+
const reactions = new Map(this.database.prepare(`WITH target AS (
|
|
139
|
+
SELECT o.id AS outbox_id, o.channel, o.account, o.provider_message_id, b.conversation_hash, b.principal_id
|
|
140
|
+
FROM json_each(?) j JOIN outbox_messages o ON o.id=j.value JOIN conversation_bindings b ON b.id=o.binding_id
|
|
141
|
+
WHERE o.provider_message_id IS NOT NULL)
|
|
142
|
+
SELECT t.outbox_id, min(i.received_at) AS first_at, count(*) AS count
|
|
143
|
+
FROM inbox_messages i
|
|
144
|
+
JOIN conversation_bindings ib ON ib.id=i.binding_id
|
|
145
|
+
JOIN target t ON t.channel=i.channel AND t.account=i.account
|
|
146
|
+
AND t.provider_message_id=json_extract(i.envelope_json,'$.metadata.replyToProviderMessageId')
|
|
147
|
+
AND ib.conversation_hash=t.conversation_hash AND ib.principal_id=t.principal_id
|
|
148
|
+
GROUP BY t.outbox_id`).all(ids)
|
|
149
|
+
.map(row => [row.outbox_id, row]));
|
|
150
|
+
const feedback = new Map();
|
|
151
|
+
for (const row of this.database.prepare(`SELECT json_extract(source_json,'$.target.outboxId') AS outbox_id,
|
|
152
|
+
json_extract(canonical_json,'$.version') AS version, json_extract(canonical_json,'$.objectiveStatus') AS status
|
|
153
|
+
FROM delivery_work_item_feedback WHERE state='recorded'
|
|
154
|
+
AND json_extract(source_json,'$.target.outboxId') IN (SELECT value FROM json_each(?))
|
|
155
|
+
ORDER BY version DESC, admission_sequence DESC`).all(ids)) {
|
|
156
|
+
if (!feedback.has(row.outbox_id))
|
|
157
|
+
feedback.set(row.outbox_id, row);
|
|
158
|
+
}
|
|
159
|
+
return Object.freeze(audits.map(audit => this.fact(audit, reactions.get(audit.outboxId), feedback.get(audit.outboxId)))
|
|
160
|
+
.filter(item => item !== undefined));
|
|
161
|
+
}
|
|
162
|
+
fact(audit, reaction, feedback) {
|
|
163
|
+
const outbox = this.database.prepare(`SELECT o.status, o.channel, o.account, o.provider_message_id, o.updated_at,
|
|
164
|
+
b.workspace, b.agent_preset, o.idempotency_key,
|
|
165
|
+
json_extract(o.intent_json,?) AS linked_report, json_extract(o.intent_json,?) AS linked_origin
|
|
166
|
+
FROM outbox_messages o JOIN conversation_bindings b ON b.id=o.binding_id WHERE o.id=?`)
|
|
167
|
+
.get(`$.metadata."${linkedReportMetadataPrefix}"`, `$.metadata."${linkedReportMetadataPrefix}.originBindingId"`, audit.outboxId);
|
|
168
|
+
if (outbox === undefined)
|
|
169
|
+
return undefined;
|
|
170
|
+
const accepted = this.database.prepare(`SELECT min(finished_at) AS at FROM outbox_attempts
|
|
171
|
+
WHERE outbox_id=? AND status IN ('accepted','reconciled') AND finished_at IS NOT NULL`).get(audit.outboxId).at;
|
|
172
|
+
const receipts = outbox.provider_message_id === null ? [] : this.database.prepare(`SELECT status, occurred_at FROM delivery_receipts
|
|
173
|
+
WHERE channel=? AND account=? AND provider_message_id=?`).all(outbox.channel, outbox.account, outbox.provider_message_id);
|
|
174
|
+
const receipt = (status) => receipts.find(row => row.status === status)?.occurred_at;
|
|
175
|
+
const sent = ['accepted', 'delivered', 'read'].includes(outbox.status);
|
|
176
|
+
const acceptedAt = accepted ?? receipt('accepted') ?? (sent ? outbox.updated_at : undefined);
|
|
177
|
+
const deliveredAt = receipt('delivered') ?? (outbox.status === 'delivered' || outbox.status === 'read' ? outbox.updated_at : undefined);
|
|
178
|
+
const readAt = receipt('read') ?? (outbox.status === 'read' ? outbox.updated_at : undefined);
|
|
179
|
+
// Same precedence as Delivery's own objective state: the newest accepted
|
|
180
|
+
// owner command, else the legacy first verified-task receipt as version 1.
|
|
181
|
+
const owner = feedback !== undefined ? undefined : this.database.prepare(`SELECT version, objective_status AS status
|
|
182
|
+
FROM delivery_owner_objective_commands WHERE source_outbox_id=? AND json_extract(result_json,'$.error') IS NULL
|
|
183
|
+
ORDER BY version DESC LIMIT 1`).get(audit.outboxId)
|
|
184
|
+
?? this.database.prepare(`SELECT 1 AS version, objective_status AS status FROM workflow_verified_task_feedback
|
|
185
|
+
WHERE source_outbox_id=?`).get(audit.outboxId);
|
|
186
|
+
// Only `routeWorkItemReport` writes this reserved marker + key prefix;
|
|
187
|
+
// every public enqueue rejects both.
|
|
188
|
+
const linkedOrigin = outbox.linked_report === 'v1' && outbox.idempotency_key.startsWith(linkedReportKeyPrefix)
|
|
189
|
+
&& typeof outbox.linked_origin === 'string' && outbox.linked_origin.length > 0 ? outbox.linked_origin : undefined;
|
|
190
|
+
const judged = feedback ?? owner;
|
|
191
|
+
const objectiveStatus = (status) => status === 'achieved' || status === 'partial' || status === 'not-achieved' ? status : 'unknown';
|
|
192
|
+
return Object.freeze({
|
|
193
|
+
auditSequence: audit.sequence, outboxId: audit.outboxId, bindingId: audit.bindingId,
|
|
194
|
+
scope: Object.freeze({ workspace: outbox.workspace, preset: outbox.agent_preset }),
|
|
195
|
+
lane: audit.lane, sourceId: audit.sourceId, purpose: audit.purpose, purposeSource: audit.purposeSource,
|
|
196
|
+
...(audit.workItemId === undefined ? {} : { workItemId: audit.workItemId }),
|
|
197
|
+
...(audit.questionId === undefined ? {} : { questionId: audit.questionId }),
|
|
198
|
+
...(audit.answersQuestionId === undefined ? {} : { answersQuestionId: audit.answersQuestionId }),
|
|
199
|
+
verdict: audit.verdict, reasons: audit.reasons, auditedAt: audit.recordedAt,
|
|
200
|
+
transport: Object.freeze({ status: outbox.status,
|
|
201
|
+
...(acceptedAt === undefined ? {} : { acceptedAt }),
|
|
202
|
+
...(deliveredAt === undefined ? {} : { deliveredAt }),
|
|
203
|
+
...(readAt === undefined ? {} : { readAt }) }),
|
|
204
|
+
...(reaction === undefined || outbox.provider_message_id === null ? {} : { reaction: Object.freeze({ firstAt: reaction.first_at, count: reaction.count }) }),
|
|
205
|
+
...(judged === undefined || !Number.isSafeInteger(judged.version) ? {} : { judgement: Object.freeze({
|
|
206
|
+
source: feedback !== undefined ? 'work-item-feedback' : 'owner-objective',
|
|
207
|
+
version: judged.version, objectiveStatus: objectiveStatus(judged.status)
|
|
208
|
+
}) }),
|
|
209
|
+
...(linkedOrigin === undefined ? {} : { route: Object.freeze({ kind: 'linked', originBindingId: linkedOrigin }) }),
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
//# sourceMappingURL=attention-audit-store.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attention-audit-store.js","sourceRoot":"","sources":["../src/attention-audit-store.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WP19 stage 3: purpose enforcement (opt-in Delivery config `attentionEnforce`,
|
|
3
|
+
* default off). With it on, a background/owner notice whose purpose is missing
|
|
4
|
+
* or fails the Host checks, or that exceeds its caps while
|
|
5
|
+
* `attentionAttachOverCap` is off, is rejected before it is queued.
|
|
6
|
+
*
|
|
7
|
+
* A rejected notice never becomes an Outbox row, and the v31
|
|
8
|
+
* `delivery_attention_audit` table is keyed by `outbox_id` with an Outbox
|
|
9
|
+
* foreign key and a closed `verdict` CHECK, so it cannot hold a `rejected`
|
|
10
|
+
* verdict without a schema bump. Rejections are therefore recorded in this
|
|
11
|
+
* process-local, bounded, non-durable log, like the stage 2c progress audit.
|
|
12
|
+
* A rejection never opens or settles a question, so the durable ledger that
|
|
13
|
+
* decides it is unchanged by it and a retry after restart is decided again
|
|
14
|
+
* the same way.
|
|
15
|
+
*/
|
|
16
|
+
import { type AttentionAssessment, type AttentionAuditReason, type AttentionLane } from './attention.js';
|
|
17
|
+
/** Enforcement-only reasons, appended after the audit reasons. */
|
|
18
|
+
export declare const attentionEnforcementOnlyReasons: readonly ["split-evasion", "attachment-unsupported"];
|
|
19
|
+
export type AttentionRejectionReason = AttentionAuditReason | typeof attentionEnforcementOnlyReasons[number];
|
|
20
|
+
/** Purpose reasons that reject under enforcement: the stage 1 would-reject set plus an undeclared purpose. */
|
|
21
|
+
export declare const attentionEnforcedPurposeReasons: ReadonlySet<AttentionAuditReason>;
|
|
22
|
+
/**
|
|
23
|
+
* Pure: reasons one assessment is rejected for, in fixed order. Over-cap text
|
|
24
|
+
* is rejected only when it cannot take the stage 2b attachment path.
|
|
25
|
+
* `text-exposes-*` stay audit-only.
|
|
26
|
+
*/
|
|
27
|
+
export declare function attentionEnforcementReasons(assessment: Readonly<Pick<AttentionAssessment, 'reasons'>>, attachOverCap: boolean): readonly AttentionAuditReason[];
|
|
28
|
+
export declare function orderAttentionRejectionReasons(reasons: Iterable<AttentionRejectionReason>): readonly AttentionRejectionReason[];
|
|
29
|
+
export interface AttentionRejectionEntry {
|
|
30
|
+
/** Process-local read cursor, strictly increasing from 1. */
|
|
31
|
+
sequence: number;
|
|
32
|
+
bindingId: string;
|
|
33
|
+
lane: AttentionLane;
|
|
34
|
+
sourceId: string;
|
|
35
|
+
idempotencyKey: string;
|
|
36
|
+
purpose: AttentionAssessment['purpose'];
|
|
37
|
+
purposeSource: AttentionAssessment['purposeSource'];
|
|
38
|
+
workItemId?: string;
|
|
39
|
+
questionId?: string;
|
|
40
|
+
answersQuestionId?: string;
|
|
41
|
+
wordCount: number;
|
|
42
|
+
wordCap: number;
|
|
43
|
+
verdict: 'rejected';
|
|
44
|
+
reasons: readonly AttentionRejectionReason[];
|
|
45
|
+
recordedAt: number;
|
|
46
|
+
}
|
|
47
|
+
export interface AttentionRejectionPage {
|
|
48
|
+
entries: readonly AttentionRejectionEntry[];
|
|
49
|
+
hasMore: boolean;
|
|
50
|
+
nextAfterSequence: number | null;
|
|
51
|
+
/** Always false: entries live in this Host process only. */
|
|
52
|
+
durable: false;
|
|
53
|
+
/** Entries evicted from the bounded ring since this Host started. */
|
|
54
|
+
evicted: number;
|
|
55
|
+
}
|
|
56
|
+
export declare class AttentionRejectionLog {
|
|
57
|
+
private readonly maxEntries;
|
|
58
|
+
private readonly now;
|
|
59
|
+
private readonly entries;
|
|
60
|
+
private sequence;
|
|
61
|
+
private evictedEntries;
|
|
62
|
+
constructor(maxEntries?: number, now?: () => number);
|
|
63
|
+
/** A retry of the same key rejected for the same reasons returns the retained entry instead of appending. */
|
|
64
|
+
record(input: Readonly<{
|
|
65
|
+
bindingId: string;
|
|
66
|
+
lane: AttentionLane;
|
|
67
|
+
sourceId: string;
|
|
68
|
+
idempotencyKey: string;
|
|
69
|
+
assessment: Readonly<AttentionAssessment>;
|
|
70
|
+
reasons: readonly AttentionRejectionReason[];
|
|
71
|
+
}>): AttentionRejectionEntry;
|
|
72
|
+
/** `afterSequence`/`limit` are validated by the caller. */
|
|
73
|
+
list(afterSequence: number, limit: number): AttentionRejectionPage;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=attention-enforcement.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attention-enforcement.d.ts","sourceRoot":"","sources":["../src/attention-enforcement.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AACH,OAAO,EAAyB,KAAK,mBAAmB,EAAE,KAAK,oBAAoB,EAAE,KAAK,aAAa,EAAE,MAAM,gBAAgB,CAAA;AAE/H,kEAAkE;AAClE,eAAO,MAAM,+BAA+B,sDAAuD,CAAA;AACnG,MAAM,MAAM,wBAAwB,GAAG,oBAAoB,GAAG,OAAO,+BAA+B,CAAC,MAAM,CAAC,CAAA;AAG5G,8GAA8G;AAC9G,eAAO,MAAM,+BAA+B,EAAE,WAAW,CAAC,oBAAoB,CAI5E,CAAA;AAIF;;;;GAIG;AACH,wBAAgB,2BAA2B,CAAC,UAAU,EAAE,QAAQ,CAAC,IAAI,CAAC,mBAAmB,EAAE,SAAS,CAAC,CAAC,EAAE,aAAa,EAAE,OAAO,GAAG,SAAS,oBAAoB,EAAE,CAG/J;AAED,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,QAAQ,CAAC,wBAAwB,CAAC,GAAG,SAAS,wBAAwB,EAAE,CAG/H;AAED,MAAM,WAAW,uBAAuB;IACtC,6DAA6D;IAC7D,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAA;IACjB,IAAI,EAAE,aAAa,CAAA;IACnB,QAAQ,EAAE,MAAM,CAAA;IAChB,cAAc,EAAE,MAAM,CAAA;IACtB,OAAO,EAAE,mBAAmB,CAAC,SAAS,CAAC,CAAA;IACvC,aAAa,EAAE,mBAAmB,CAAC,eAAe,CAAC,CAAA;IACnD,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,iBAAiB,CAAC,EAAE,MAAM,CAAA;IAC1B,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,OAAO,EAAE,UAAU,CAAA;IACnB,OAAO,EAAE,SAAS,wBAAwB,EAAE,CAAA;IAC5C,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,SAAS,uBAAuB,EAAE,CAAA;IAC3C,OAAO,EAAE,OAAO,CAAA;IAChB,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,4DAA4D;IAC5D,OAAO,EAAE,KAAK,CAAA;IACd,qEAAqE;IACrE,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,qBAAa,qBAAqB;IAKpB,OAAO,CAAC,QAAQ,CAAC,UAAU;IAAS,OAAO,CAAC,QAAQ,CAAC,GAAG;IAJpE,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAgC;IACxD,OAAO,CAAC,QAAQ,CAAI;IACpB,OAAO,CAAC,cAAc,CAAI;gBAEG,UAAU,SAAO,EAAmB,GAAG,GAAE,MAAM,MAAiB;IAE7F,6GAA6G;IAC7G,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,aAAa,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAA;QACvG,UAAU,EAAE,QAAQ,CAAC,mBAAmB,CAAC,CAAC;QAAC,OAAO,EAAE,SAAS,wBAAwB,EAAE,CAAA;KAAE,CAAC,GAAG,uBAAuB;IAoBtH,2DAA2D;IAC3D,IAAI,CAAC,aAAa,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,sBAAsB;CAQnE"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* WP19 stage 3: purpose enforcement (opt-in Delivery config `attentionEnforce`,
|
|
3
|
+
* default off). With it on, a background/owner notice whose purpose is missing
|
|
4
|
+
* or fails the Host checks, or that exceeds its caps while
|
|
5
|
+
* `attentionAttachOverCap` is off, is rejected before it is queued.
|
|
6
|
+
*
|
|
7
|
+
* A rejected notice never becomes an Outbox row, and the v31
|
|
8
|
+
* `delivery_attention_audit` table is keyed by `outbox_id` with an Outbox
|
|
9
|
+
* foreign key and a closed `verdict` CHECK, so it cannot hold a `rejected`
|
|
10
|
+
* verdict without a schema bump. Rejections are therefore recorded in this
|
|
11
|
+
* process-local, bounded, non-durable log, like the stage 2c progress audit.
|
|
12
|
+
* A rejection never opens or settles a question, so the durable ledger that
|
|
13
|
+
* decides it is unchanged by it and a retry after restart is decided again
|
|
14
|
+
* the same way.
|
|
15
|
+
*/
|
|
16
|
+
import { attentionAuditReasons } from './attention.js';
|
|
17
|
+
/** Enforcement-only reasons, appended after the audit reasons. */
|
|
18
|
+
export const attentionEnforcementOnlyReasons = ['split-evasion', 'attachment-unsupported'];
|
|
19
|
+
const rejectionReasonOrder = [...attentionAuditReasons, ...attentionEnforcementOnlyReasons];
|
|
20
|
+
/** Purpose reasons that reject under enforcement: the stage 1 would-reject set plus an undeclared purpose. */
|
|
21
|
+
export const attentionEnforcedPurposeReasons = new Set([
|
|
22
|
+
'missing-purpose', 'invalid-envelope', 'attention-missing-work-item', 'attention-question-invalid', 'attention-question-not-current',
|
|
23
|
+
'attention-question-already-answered', 'answer-missing-question', 'answer-unknown-question', 'answer-already-used',
|
|
24
|
+
'answer-work-item-mismatch', 'attention-question-reused',
|
|
25
|
+
]);
|
|
26
|
+
const capReasons = new Set(['over-word-cap', 'over-grapheme-cap']);
|
|
27
|
+
/**
|
|
28
|
+
* Pure: reasons one assessment is rejected for, in fixed order. Over-cap text
|
|
29
|
+
* is rejected only when it cannot take the stage 2b attachment path.
|
|
30
|
+
* `text-exposes-*` stay audit-only.
|
|
31
|
+
*/
|
|
32
|
+
export function attentionEnforcementReasons(assessment, attachOverCap) {
|
|
33
|
+
return Object.freeze(assessment.reasons.filter(reason => attentionEnforcedPurposeReasons.has(reason)
|
|
34
|
+
|| (!attachOverCap && capReasons.has(reason))));
|
|
35
|
+
}
|
|
36
|
+
export function orderAttentionRejectionReasons(reasons) {
|
|
37
|
+
const set = new Set(reasons);
|
|
38
|
+
return Object.freeze(rejectionReasonOrder.filter(reason => set.has(reason)));
|
|
39
|
+
}
|
|
40
|
+
export class AttentionRejectionLog {
|
|
41
|
+
maxEntries;
|
|
42
|
+
now;
|
|
43
|
+
entries = [];
|
|
44
|
+
sequence = 0;
|
|
45
|
+
evictedEntries = 0;
|
|
46
|
+
constructor(maxEntries = 1000, now = Date.now) {
|
|
47
|
+
this.maxEntries = maxEntries;
|
|
48
|
+
this.now = now;
|
|
49
|
+
}
|
|
50
|
+
/** A retry of the same key rejected for the same reasons returns the retained entry instead of appending. */
|
|
51
|
+
record(input) {
|
|
52
|
+
const reasons = orderAttentionRejectionReasons(input.reasons);
|
|
53
|
+
const retained = this.entries.findLast(entry => entry.bindingId === input.bindingId && entry.lane === input.lane
|
|
54
|
+
&& entry.idempotencyKey === input.idempotencyKey);
|
|
55
|
+
if (retained !== undefined && retained.sourceId === input.sourceId && retained.reasons.join() === reasons.join())
|
|
56
|
+
return retained;
|
|
57
|
+
const { assessment } = input;
|
|
58
|
+
this.sequence += 1;
|
|
59
|
+
const entry = Object.freeze({
|
|
60
|
+
sequence: this.sequence, bindingId: input.bindingId, lane: input.lane, sourceId: input.sourceId, idempotencyKey: input.idempotencyKey,
|
|
61
|
+
purpose: assessment.purpose, purposeSource: assessment.purposeSource,
|
|
62
|
+
...(assessment.workItemId === undefined ? {} : { workItemId: assessment.workItemId }),
|
|
63
|
+
...(assessment.questionId === undefined ? {} : { questionId: assessment.questionId }),
|
|
64
|
+
...(assessment.answersQuestionId === undefined ? {} : { answersQuestionId: assessment.answersQuestionId }),
|
|
65
|
+
wordCount: assessment.wordCount, wordCap: assessment.wordCap, verdict: 'rejected', reasons, recordedAt: this.now(),
|
|
66
|
+
});
|
|
67
|
+
this.entries.push(entry);
|
|
68
|
+
while (this.entries.length > this.maxEntries) {
|
|
69
|
+
this.entries.shift();
|
|
70
|
+
this.evictedEntries += 1;
|
|
71
|
+
}
|
|
72
|
+
return entry;
|
|
73
|
+
}
|
|
74
|
+
/** `afterSequence`/`limit` are validated by the caller. */
|
|
75
|
+
list(afterSequence, limit) {
|
|
76
|
+
const start = this.entries.findIndex(entry => entry.sequence > afterSequence);
|
|
77
|
+
const remaining = start < 0 ? [] : this.entries.slice(start);
|
|
78
|
+
const entries = Object.freeze(remaining.slice(0, limit));
|
|
79
|
+
const hasMore = remaining.length > limit;
|
|
80
|
+
return Object.freeze({ entries, hasMore, nextAfterSequence: hasMore ? entries.at(-1).sequence : null, durable: false,
|
|
81
|
+
evicted: this.evictedEntries });
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=attention-enforcement.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"attention-enforcement.js","sourceRoot":"","sources":["../src/attention-enforcement.ts"],"names":[],"mappings":"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"}
|