@wirecat/cli-core 0.536.0 → 0.538.0
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/CHANGELOG.md +41 -0
- package/dist/calendar/cli/answers.d.ts +143 -0
- package/dist/calendar/cli/answers.js +28 -1
- package/dist/calendar/cli/index.js +35 -0
- package/dist/calendar/prepare.d.ts +134 -0
- package/dist/calendar/prepare.js +179 -0
- package/dist/calendar/testing/answers.d.ts +2 -1
- package/dist/calendar/testing/answers.js +3 -2
- package/dist/entities/contact-context.d.ts +35 -0
- package/dist/entities/contact-context.js +3 -3
- package/dist/errors/reasons.d.ts +1 -1
- package/dist/errors/reasons.js +6 -0
- package/dist/mail/server-search.js +20 -4
- package/dist/meetings/mcp/index.d.ts +15 -15
- package/dist/oauth/index.d.ts +1 -1
- package/dist/oauth/index.js +1 -1
- package/dist/oauth/keeper.d.ts +9 -2
- package/dist/oauth/keeper.js +29 -15
- package/dist/services/person-context.d.ts +6 -1
- package/dist/services/person-context.js +9 -3
- package/dist/store/sqlite/evidence-findings.js +4 -2
- package/dist/store/sqlite/task-board.js +16 -0
- package/dist/store/sqlite/work-tasks.d.ts +4 -1
- package/dist/store/sqlite/work-tasks.js +6 -2
- package/package.json +1 -1
|
@@ -1,4 +1,26 @@
|
|
|
1
1
|
import * as v from "valibot";
|
|
2
|
+
export declare const contextMessage: v.StrictObjectSchema<{
|
|
3
|
+
readonly locator: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>;
|
|
4
|
+
readonly provider: v.StringSchema<undefined>;
|
|
5
|
+
readonly account: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
|
|
6
|
+
readonly chat_id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
|
|
7
|
+
readonly chat_title: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
8
|
+
readonly id: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
|
|
9
|
+
readonly timestamp: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>;
|
|
10
|
+
readonly sender_id: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>, undefined>;
|
|
11
|
+
readonly sender_name: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
12
|
+
readonly outgoing: v.NullableSchema<v.BooleanSchema<undefined>, undefined>;
|
|
13
|
+
readonly text: v.StringSchema<undefined>;
|
|
14
|
+
}, undefined>;
|
|
15
|
+
export declare const contextEvent: v.StrictObjectSchema<{
|
|
16
|
+
readonly ref: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>;
|
|
17
|
+
readonly title: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
18
|
+
readonly starts_at: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
|
|
19
|
+
readonly ends_at: v.NullableSchema<v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.IsoTimestampAction<string, "an ISO 8601 time in UTC">, v.EndsWithAction<string, "Z", "an ISO 8601 time in UTC, ending in `Z`">]>, undefined>;
|
|
20
|
+
readonly all_day: v.BooleanSchema<undefined>;
|
|
21
|
+
readonly location: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
22
|
+
readonly status: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
23
|
+
}, undefined>;
|
|
2
24
|
export declare const contactMessages: v.StrictObjectSchema<{
|
|
3
25
|
readonly person: v.StrictObjectSchema<{
|
|
4
26
|
readonly uid: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
|
|
@@ -1401,6 +1423,19 @@ export declare const contactContext: v.UnionSchema<[v.StrictObjectSchema<{
|
|
|
1401
1423
|
readonly messages: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
1402
1424
|
}, undefined>;
|
|
1403
1425
|
}, undefined>], undefined>;
|
|
1426
|
+
export declare const meetingCue: v.StrictObjectSchema<{
|
|
1427
|
+
readonly reference: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.RegexAction<string, "a reference such as `note:42`">]>;
|
|
1428
|
+
readonly fingerprint: v.StringSchema<undefined>;
|
|
1429
|
+
readonly transcript_id: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
1430
|
+
readonly content_hash: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
1431
|
+
readonly position: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
1432
|
+
readonly start_ms: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
1433
|
+
readonly end_ms: v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>;
|
|
1434
|
+
readonly speaker_participant_id: v.NullableSchema<v.SchemaWithPipe<readonly [v.NumberSchema<undefined>, v.IntegerAction<number, undefined>, v.MinValueAction<number, 0, undefined>]>, undefined>;
|
|
1435
|
+
readonly speaker_name: v.NullableSchema<v.StringSchema<undefined>, undefined>;
|
|
1436
|
+
readonly text: v.StringSchema<undefined>;
|
|
1437
|
+
readonly revision: v.PicklistSchema<["current", "superseded"], undefined>;
|
|
1438
|
+
}, undefined>;
|
|
1404
1439
|
export declare const contactPreparation: v.StrictObjectSchema<{
|
|
1405
1440
|
readonly person: v.StrictObjectSchema<{
|
|
1406
1441
|
readonly uid: v.SchemaWithPipe<readonly [v.StringSchema<undefined>, v.MinLengthAction<string, 1, undefined>]>;
|
|
@@ -4,7 +4,7 @@ import { linkedPerson } from "./contact-activity.js";
|
|
|
4
4
|
import { count, id, reference, text, time } from "./fields.js";
|
|
5
5
|
import { message } from "./message.js";
|
|
6
6
|
const contextPerson = v.strictObject({ ...linkedPerson.entries, provider: text, id });
|
|
7
|
-
const contextMessage = v.strictObject({
|
|
7
|
+
export const contextMessage = v.strictObject({
|
|
8
8
|
locator: reference,
|
|
9
9
|
provider: text,
|
|
10
10
|
account: id,
|
|
@@ -25,7 +25,7 @@ const sharedChat = v.strictObject({
|
|
|
25
25
|
kind: chat.entries.kind,
|
|
26
26
|
last_message_at: v.nullable(time),
|
|
27
27
|
});
|
|
28
|
-
const contextEvent = v.strictObject({
|
|
28
|
+
export const contextEvent = v.strictObject({
|
|
29
29
|
ref: reference,
|
|
30
30
|
title: v.nullable(text),
|
|
31
31
|
starts_at: v.nullable(time),
|
|
@@ -124,7 +124,7 @@ const coverage = v.strictObject({
|
|
|
124
124
|
has_more: v.boolean(),
|
|
125
125
|
cut_by: v.nullable(v.picklist(["limit", "bytes"])),
|
|
126
126
|
});
|
|
127
|
-
const meetingCue = v.strictObject({
|
|
127
|
+
export const meetingCue = v.strictObject({
|
|
128
128
|
reference,
|
|
129
129
|
fingerprint: text,
|
|
130
130
|
transcript_id: count,
|
package/dist/errors/reasons.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export declare const REASONS: ErrorReason[];
|
|
|
7
7
|
* Detail keys an error from this package may carry, until each is registered on its own reason. Not `cause`:
|
|
8
8
|
* that can hold a provider's or SQLite's raw text.
|
|
9
9
|
*/
|
|
10
|
-
export declare const DETAILS: readonly ["contracts", "calendars", "allowed", "alternative", "arguments", "at", "available", "batch", "budget", "built", "candidates", "chat", "chat_id", "cli", "complete", "emitted_bytes", "exact", "forum", "guide", "hint", "id", "index", "kind", "limit", "limit_source", "max_bytes", "model", "name", "operation_id", "operation_ids", "text_limit", "store_refused_in", "partial_output", "path", "permission", "person_ids", "previous_chat_id", "profiles", "provider_error", "provider", "recovery", "reference", "remembered", "retry_at", "retry_after_ms", "runtime", "saved", "scheduled_for", "script", "sent", "sent_count", "settlement_failed", "span", "sqlite", "stage", "standing", "state", "stderr", "term", "total", "upgraded", "wanted"];
|
|
10
|
+
export declare const DETAILS: readonly ["contracts", "calendars", "allowed", "alternative", "arguments", "at", "available", "batch", "budget", "built", "candidates", "chat", "chat_id", "cli", "complete", "emitted_bytes", "exact", "forum", "guide", "hint", "id", "index", "kind", "limit", "limit_source", "max_bytes", "model", "name", "operation_id", "operation_ids", "text_limit", "store_refused_in", "subtasks", "partial_output", "parent", "path", "permission", "person_ids", "previous_chat_id", "profiles", "provider_error", "provider", "recovery", "reference", "remembered", "retry_at", "retry_after_ms", "runtime", "saved", "scheduled_for", "script", "sent", "sent_count", "settlement_failed", "span", "sqlite", "stage", "standing", "state", "stderr", "term", "total", "upgraded", "wanted"];
|
|
11
11
|
/** What a CLI adds to the shared registry: its own reasons, and the detail keys its adapters raise. */
|
|
12
12
|
export interface AppErrors {
|
|
13
13
|
reasons?: readonly ErrorReason[];
|
package/dist/errors/reasons.js
CHANGED
|
@@ -65,6 +65,9 @@ const OTHERS = [
|
|
|
65
65
|
["calendar.read-only", "permission_error", "the calendar takes no new events from this account"],
|
|
66
66
|
["calendar.unsynced", "not_found", "the calendar account has no synced calendar in the store"],
|
|
67
67
|
["calendar.sync-incomplete", "not_found", "a synced calendar's sync never finished or is mid-round"],
|
|
68
|
+
["calendar.no-upcoming", "not_found", "no upcoming meeting with other attendees is stored for this account"],
|
|
69
|
+
["chat.not-read", "not_found", "a chat shared with this person is not held whole in the store"],
|
|
70
|
+
["meetings.index-pending", "not_found", "some stored meetings are not indexed by person yet"],
|
|
68
71
|
["profile.locked", "permission_error", "a profile lock pins this process to its own account"],
|
|
69
72
|
["busy.offline", "permission_error", "calendars busy asks the provider; --offline was given"],
|
|
70
73
|
validation("busy.no-calendars", "no calendar syncs and no person was named, so there is nobody to ask"),
|
|
@@ -134,6 +137,7 @@ const OTHERS = [
|
|
|
134
137
|
validation("task.column", "the task beside which it moves is in another column than the status asked for"),
|
|
135
138
|
validation("task.dependency-cycle", "the task would depend on itself"),
|
|
136
139
|
validation("task.has-subtasks", "the task has subtasks; move or detach them before it moves to another project"),
|
|
140
|
+
validation("task.is-subtask", "a subtask moves to another project only when it is detached from its parent in the same write"),
|
|
137
141
|
validation("task.duplicate-candidates", "open tasks may be the same job: attach to one, or add it as new"),
|
|
138
142
|
validation("task.certainty-required", "an agent closes a task with its certainty, 0 to 1"),
|
|
139
143
|
["task.pair-decided", "permission_error", "the owner already decided the pair; an agent does not change it"],
|
|
@@ -210,7 +214,9 @@ export const DETAILS = [
|
|
|
210
214
|
"operation_ids",
|
|
211
215
|
"text_limit",
|
|
212
216
|
"store_refused_in",
|
|
217
|
+
"subtasks",
|
|
213
218
|
"partial_output",
|
|
219
|
+
"parent",
|
|
214
220
|
"path",
|
|
215
221
|
"permission",
|
|
216
222
|
"person_ids",
|
|
@@ -9,7 +9,8 @@ import { factsOf } from "./facts.js";
|
|
|
9
9
|
import { mailHeadersOf } from "./headers.js";
|
|
10
10
|
import { parseMail } from "./parse.js";
|
|
11
11
|
import { storedMailRules } from "./rule-stored.js";
|
|
12
|
-
import {
|
|
12
|
+
import { ruleTagger, settleRuleTags } from "./rule-tags.js";
|
|
13
|
+
import { decide, tagsOf } from "./rules.js";
|
|
13
14
|
import { mailCriteriaOf } from "./server-query.js";
|
|
14
15
|
const refusal = {
|
|
15
16
|
unsupported: "this mailbox's server cannot be searched — use --backend archive",
|
|
@@ -74,6 +75,8 @@ export const searchMailServer = async (store, mailbox, access, request) => {
|
|
|
74
75
|
};
|
|
75
76
|
const accountId = await store.accounts.saveAccount(mailbox, { name: mailbox.account });
|
|
76
77
|
const fresh = [];
|
|
78
|
+
let rules = [];
|
|
79
|
+
const ruled = new Map();
|
|
77
80
|
let raws;
|
|
78
81
|
let opening;
|
|
79
82
|
let openFailure;
|
|
@@ -124,7 +127,7 @@ export const searchMailServer = async (store, mailbox, access, request) => {
|
|
|
124
127
|
if (!open)
|
|
125
128
|
return;
|
|
126
129
|
const unique = [...new Map(listed.map((item) => [item.messageId, item])).values()];
|
|
127
|
-
|
|
130
|
+
rules = await storedMailRules(store, { address: mailbox.account });
|
|
128
131
|
const facts = factsOf(unique, rules);
|
|
129
132
|
for (const item of unique) {
|
|
130
133
|
const held = (await store.mail.email(accountId, item.messageId)) !== null;
|
|
@@ -133,8 +136,10 @@ export const searchMailServer = async (store, mailbox, access, request) => {
|
|
|
133
136
|
continue;
|
|
134
137
|
report.returned += 1;
|
|
135
138
|
sources.set(sourceKey(item.threadId, item.messageId), held ? "both" : "server");
|
|
136
|
-
if (
|
|
137
|
-
|
|
139
|
+
if (held)
|
|
140
|
+
continue;
|
|
141
|
+
fresh.push(item);
|
|
142
|
+
ruled.set(item, { ruleId: rule?.id ?? null, tags: tagsOf(rules, facts(item)) });
|
|
138
143
|
}
|
|
139
144
|
if (fresh.length)
|
|
140
145
|
raws = await source.fetchRaw(fresh);
|
|
@@ -175,6 +180,7 @@ export const searchMailServer = async (store, mailbox, access, request) => {
|
|
|
175
180
|
}
|
|
176
181
|
const now = Date.now();
|
|
177
182
|
const inputs = [];
|
|
183
|
+
const saved = [];
|
|
178
184
|
for (const [index, item] of fresh.entries()) {
|
|
179
185
|
const raw = raws?.[index];
|
|
180
186
|
try {
|
|
@@ -201,10 +207,12 @@ export const searchMailServer = async (store, mailbox, access, request) => {
|
|
|
201
207
|
: { locations: [{ folder, uidvalidity: validity, uid: Number(item.uid) }] }),
|
|
202
208
|
...(item.seen === undefined ? {} : { read: item.seen }),
|
|
203
209
|
...(item.flagged === undefined ? {} : { flagged: item.flagged }),
|
|
210
|
+
mailRuleId: ruled.get(item)?.ruleId ?? null,
|
|
204
211
|
attachments: files,
|
|
205
212
|
},
|
|
206
213
|
],
|
|
207
214
|
});
|
|
215
|
+
saved.push(item);
|
|
208
216
|
}
|
|
209
217
|
catch {
|
|
210
218
|
sources.delete(sourceKey(item.threadId, item.messageId));
|
|
@@ -213,6 +221,14 @@ export const searchMailServer = async (store, mailbox, access, request) => {
|
|
|
213
221
|
}
|
|
214
222
|
if (inputs.length)
|
|
215
223
|
await store.mail.saveThreads(inputs);
|
|
224
|
+
// Sync never reads mail whose place it knows again, so a rule's tags go on here or never.
|
|
225
|
+
const tagger = ruleTagger(store, mailbox);
|
|
226
|
+
for (const item of saved) {
|
|
227
|
+
const tags = ruled.get(item)?.tags ?? [];
|
|
228
|
+
if (tags.length)
|
|
229
|
+
await tagger.tag({ type: "message", chatId: item.threadId, messageId: item.messageId }, tags);
|
|
230
|
+
}
|
|
231
|
+
await settleRuleTags(store, accountId, rules, tagger.outcome());
|
|
216
232
|
report.new = inputs.length;
|
|
217
233
|
report.returned = sources.size;
|
|
218
234
|
return { report, sources };
|
|
@@ -38,7 +38,6 @@ export declare const meetingTools: ({
|
|
|
38
38
|
max_read_bytes?: undefined;
|
|
39
39
|
cursor?: undefined;
|
|
40
40
|
} | {
|
|
41
|
-
limit?: undefined;
|
|
42
41
|
event_id?: undefined;
|
|
43
42
|
series_id?: undefined;
|
|
44
43
|
page?: undefined;
|
|
@@ -54,11 +53,11 @@ export declare const meetingTools: ({
|
|
|
54
53
|
format?: undefined;
|
|
55
54
|
transcript_id?: undefined;
|
|
56
55
|
query?: undefined;
|
|
56
|
+
limit?: undefined;
|
|
57
57
|
scan_limit?: undefined;
|
|
58
58
|
max_read_bytes?: undefined;
|
|
59
59
|
cursor?: undefined;
|
|
60
60
|
} | {
|
|
61
|
-
limit?: undefined;
|
|
62
61
|
event_id?: undefined;
|
|
63
62
|
series_id?: undefined;
|
|
64
63
|
page?: undefined;
|
|
@@ -76,11 +75,11 @@ export declare const meetingTools: ({
|
|
|
76
75
|
format?: undefined;
|
|
77
76
|
transcript_id?: undefined;
|
|
78
77
|
query?: undefined;
|
|
78
|
+
limit?: undefined;
|
|
79
79
|
scan_limit?: undefined;
|
|
80
80
|
max_read_bytes?: undefined;
|
|
81
81
|
cursor?: undefined;
|
|
82
82
|
} | {
|
|
83
|
-
limit?: undefined;
|
|
84
83
|
event_id?: undefined;
|
|
85
84
|
series_id?: undefined;
|
|
86
85
|
page?: undefined;
|
|
@@ -102,6 +101,7 @@ export declare const meetingTools: ({
|
|
|
102
101
|
minimum: number;
|
|
103
102
|
};
|
|
104
103
|
query?: undefined;
|
|
104
|
+
limit?: undefined;
|
|
105
105
|
scan_limit?: undefined;
|
|
106
106
|
max_read_bytes?: undefined;
|
|
107
107
|
cursor?: undefined;
|
|
@@ -188,7 +188,6 @@ export declare const meetingTools: ({
|
|
|
188
188
|
max_read_bytes?: undefined;
|
|
189
189
|
cursor?: undefined;
|
|
190
190
|
} | {
|
|
191
|
-
limit?: undefined;
|
|
192
191
|
event_id?: undefined;
|
|
193
192
|
series_id?: undefined;
|
|
194
193
|
page?: undefined;
|
|
@@ -204,11 +203,11 @@ export declare const meetingTools: ({
|
|
|
204
203
|
format?: undefined;
|
|
205
204
|
transcript_id?: undefined;
|
|
206
205
|
query?: undefined;
|
|
206
|
+
limit?: undefined;
|
|
207
207
|
scan_limit?: undefined;
|
|
208
208
|
max_read_bytes?: undefined;
|
|
209
209
|
cursor?: undefined;
|
|
210
210
|
} | {
|
|
211
|
-
limit?: undefined;
|
|
212
211
|
event_id?: undefined;
|
|
213
212
|
series_id?: undefined;
|
|
214
213
|
page?: undefined;
|
|
@@ -226,11 +225,11 @@ export declare const meetingTools: ({
|
|
|
226
225
|
format?: undefined;
|
|
227
226
|
transcript_id?: undefined;
|
|
228
227
|
query?: undefined;
|
|
228
|
+
limit?: undefined;
|
|
229
229
|
scan_limit?: undefined;
|
|
230
230
|
max_read_bytes?: undefined;
|
|
231
231
|
cursor?: undefined;
|
|
232
232
|
} | {
|
|
233
|
-
limit?: undefined;
|
|
234
233
|
event_id?: undefined;
|
|
235
234
|
series_id?: undefined;
|
|
236
235
|
page?: undefined;
|
|
@@ -252,6 +251,7 @@ export declare const meetingTools: ({
|
|
|
252
251
|
minimum: number;
|
|
253
252
|
};
|
|
254
253
|
query?: undefined;
|
|
254
|
+
limit?: undefined;
|
|
255
255
|
scan_limit?: undefined;
|
|
256
256
|
max_read_bytes?: undefined;
|
|
257
257
|
cursor?: undefined;
|
|
@@ -338,7 +338,6 @@ export declare const meetingTools: ({
|
|
|
338
338
|
max_read_bytes?: undefined;
|
|
339
339
|
cursor?: undefined;
|
|
340
340
|
} | {
|
|
341
|
-
limit?: undefined;
|
|
342
341
|
event_id?: undefined;
|
|
343
342
|
series_id?: undefined;
|
|
344
343
|
page?: undefined;
|
|
@@ -354,11 +353,11 @@ export declare const meetingTools: ({
|
|
|
354
353
|
format?: undefined;
|
|
355
354
|
transcript_id?: undefined;
|
|
356
355
|
query?: undefined;
|
|
356
|
+
limit?: undefined;
|
|
357
357
|
scan_limit?: undefined;
|
|
358
358
|
max_read_bytes?: undefined;
|
|
359
359
|
cursor?: undefined;
|
|
360
360
|
} | {
|
|
361
|
-
limit?: undefined;
|
|
362
361
|
event_id?: undefined;
|
|
363
362
|
series_id?: undefined;
|
|
364
363
|
page?: undefined;
|
|
@@ -376,11 +375,11 @@ export declare const meetingTools: ({
|
|
|
376
375
|
format?: undefined;
|
|
377
376
|
transcript_id?: undefined;
|
|
378
377
|
query?: undefined;
|
|
378
|
+
limit?: undefined;
|
|
379
379
|
scan_limit?: undefined;
|
|
380
380
|
max_read_bytes?: undefined;
|
|
381
381
|
cursor?: undefined;
|
|
382
382
|
} | {
|
|
383
|
-
limit?: undefined;
|
|
384
383
|
event_id?: undefined;
|
|
385
384
|
series_id?: undefined;
|
|
386
385
|
page?: undefined;
|
|
@@ -402,6 +401,7 @@ export declare const meetingTools: ({
|
|
|
402
401
|
minimum: number;
|
|
403
402
|
};
|
|
404
403
|
query?: undefined;
|
|
404
|
+
limit?: undefined;
|
|
405
405
|
scan_limit?: undefined;
|
|
406
406
|
max_read_bytes?: undefined;
|
|
407
407
|
cursor?: undefined;
|
|
@@ -488,7 +488,6 @@ export declare const meetingTools: ({
|
|
|
488
488
|
max_read_bytes?: undefined;
|
|
489
489
|
cursor?: undefined;
|
|
490
490
|
} | {
|
|
491
|
-
limit?: undefined;
|
|
492
491
|
event_id?: undefined;
|
|
493
492
|
series_id?: undefined;
|
|
494
493
|
page?: undefined;
|
|
@@ -504,11 +503,11 @@ export declare const meetingTools: ({
|
|
|
504
503
|
format?: undefined;
|
|
505
504
|
transcript_id?: undefined;
|
|
506
505
|
query?: undefined;
|
|
506
|
+
limit?: undefined;
|
|
507
507
|
scan_limit?: undefined;
|
|
508
508
|
max_read_bytes?: undefined;
|
|
509
509
|
cursor?: undefined;
|
|
510
510
|
} | {
|
|
511
|
-
limit?: undefined;
|
|
512
511
|
event_id?: undefined;
|
|
513
512
|
series_id?: undefined;
|
|
514
513
|
page?: undefined;
|
|
@@ -526,11 +525,11 @@ export declare const meetingTools: ({
|
|
|
526
525
|
format?: undefined;
|
|
527
526
|
transcript_id?: undefined;
|
|
528
527
|
query?: undefined;
|
|
528
|
+
limit?: undefined;
|
|
529
529
|
scan_limit?: undefined;
|
|
530
530
|
max_read_bytes?: undefined;
|
|
531
531
|
cursor?: undefined;
|
|
532
532
|
} | {
|
|
533
|
-
limit?: undefined;
|
|
534
533
|
event_id?: undefined;
|
|
535
534
|
series_id?: undefined;
|
|
536
535
|
page?: undefined;
|
|
@@ -552,6 +551,7 @@ export declare const meetingTools: ({
|
|
|
552
551
|
minimum: number;
|
|
553
552
|
};
|
|
554
553
|
query?: undefined;
|
|
554
|
+
limit?: undefined;
|
|
555
555
|
scan_limit?: undefined;
|
|
556
556
|
max_read_bytes?: undefined;
|
|
557
557
|
cursor?: undefined;
|
|
@@ -638,7 +638,6 @@ export declare const meetingTools: ({
|
|
|
638
638
|
max_read_bytes?: undefined;
|
|
639
639
|
cursor?: undefined;
|
|
640
640
|
} | {
|
|
641
|
-
limit?: undefined;
|
|
642
641
|
event_id?: undefined;
|
|
643
642
|
series_id?: undefined;
|
|
644
643
|
page?: undefined;
|
|
@@ -654,11 +653,11 @@ export declare const meetingTools: ({
|
|
|
654
653
|
format?: undefined;
|
|
655
654
|
transcript_id?: undefined;
|
|
656
655
|
query?: undefined;
|
|
656
|
+
limit?: undefined;
|
|
657
657
|
scan_limit?: undefined;
|
|
658
658
|
max_read_bytes?: undefined;
|
|
659
659
|
cursor?: undefined;
|
|
660
660
|
} | {
|
|
661
|
-
limit?: undefined;
|
|
662
661
|
event_id?: undefined;
|
|
663
662
|
series_id?: undefined;
|
|
664
663
|
page?: undefined;
|
|
@@ -676,11 +675,11 @@ export declare const meetingTools: ({
|
|
|
676
675
|
format?: undefined;
|
|
677
676
|
transcript_id?: undefined;
|
|
678
677
|
query?: undefined;
|
|
678
|
+
limit?: undefined;
|
|
679
679
|
scan_limit?: undefined;
|
|
680
680
|
max_read_bytes?: undefined;
|
|
681
681
|
cursor?: undefined;
|
|
682
682
|
} | {
|
|
683
|
-
limit?: undefined;
|
|
684
683
|
event_id?: undefined;
|
|
685
684
|
series_id?: undefined;
|
|
686
685
|
page?: undefined;
|
|
@@ -702,6 +701,7 @@ export declare const meetingTools: ({
|
|
|
702
701
|
minimum: number;
|
|
703
702
|
};
|
|
704
703
|
query?: undefined;
|
|
704
|
+
limit?: undefined;
|
|
705
705
|
scan_limit?: undefined;
|
|
706
706
|
max_read_bytes?: undefined;
|
|
707
707
|
cursor?: undefined;
|
package/dist/oauth/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { type Authorization, type AuthorizeOptions, authorize } from "./authorize.js";
|
|
2
|
-
export { type SignInOptions, type TokenKeeper, type TokenKeeperOptions, tokenKeeper } from "./keeper.js";
|
|
2
|
+
export { type SignInOptions, type TokenKeeper, type TokenKeeperOptions, type TokensOptions, tokenKeeper, } from "./keeper.js";
|
|
3
3
|
export type { ExchangeContext, OAuthClient, OAuthProvider, RequestContext, TokenSet } from "./provider.js";
|
|
4
4
|
export { requestTokens, revokeToken, TOKEN_LOG_ALLOW, TOKEN_LOG_EVENT } from "./requests.js";
|
|
5
5
|
export { type CredentialStorageOptions, credentialStorage, driveSessionCodec, type EncodeInput, oauthAccount, type StoredTokens, standardCodec, type TokenCodec, type TokenStorage, zoomTokenCodec, } from "./storage.js";
|
package/dist/oauth/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
export { authorize } from "./authorize.js";
|
|
2
|
-
export { tokenKeeper } from "./keeper.js";
|
|
2
|
+
export { tokenKeeper, } from "./keeper.js";
|
|
3
3
|
export { requestTokens, revokeToken, TOKEN_LOG_ALLOW, TOKEN_LOG_EVENT } from "./requests.js";
|
|
4
4
|
export { credentialStorage, driveSessionCodec, oauthAccount, standardCodec, zoomTokenCodec, } from "./storage.js";
|
package/dist/oauth/keeper.d.ts
CHANGED
|
@@ -23,9 +23,16 @@ export type SignInOptions = Omit<AuthorizeOptions, "provider" | "signal"> & {
|
|
|
23
23
|
/** Runs before anything is saved; it may refuse the account or add fields to the entry (drive's `principal`). */
|
|
24
24
|
accept?: (tokens: TokenSet) => Promise<Record<string, unknown> | undefined> | Record<string, unknown> | undefined;
|
|
25
25
|
};
|
|
26
|
+
export interface TokensOptions {
|
|
27
|
+
/**
|
|
28
|
+
* The server refused the access token this keeper last answered, though `expiresAt` called it fresh (a revoked
|
|
29
|
+
* session): refresh under the lock anyway, unless another process already saved a different fresh one.
|
|
30
|
+
*/
|
|
31
|
+
refresh?: boolean;
|
|
32
|
+
}
|
|
26
33
|
export interface TokenKeeper {
|
|
27
|
-
accessToken(): Promise<string>;
|
|
28
|
-
tokens(): Promise<TokenSet>;
|
|
34
|
+
accessToken(options?: TokensOptions): Promise<string>;
|
|
35
|
+
tokens(options?: TokensOptions): Promise<TokenSet>;
|
|
29
36
|
signIn(options: SignInOptions): Promise<{
|
|
30
37
|
scopes: string[];
|
|
31
38
|
}>;
|
package/dist/oauth/keeper.js
CHANGED
|
@@ -87,25 +87,39 @@ export const tokenKeeper = (options) => {
|
|
|
87
87
|
return kept && fresh(kept) && kept.refreshToken === saved.refreshToken ? kept : undefined;
|
|
88
88
|
};
|
|
89
89
|
const forget = () => refreshedInMemory.delete(lockPath);
|
|
90
|
-
|
|
90
|
+
let answered;
|
|
91
|
+
const answer = (given) => {
|
|
92
|
+
answered = given;
|
|
93
|
+
return given;
|
|
94
|
+
};
|
|
95
|
+
const tokens = async ({ refresh = false } = {}) => {
|
|
91
96
|
const seen = storage.read();
|
|
92
97
|
if (!seen)
|
|
93
98
|
throw notSignedIn();
|
|
94
|
-
if (
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
99
|
+
if (!refresh) {
|
|
100
|
+
const kept = remembered(seen.tokens);
|
|
101
|
+
if (kept)
|
|
102
|
+
return answer(kept);
|
|
103
|
+
if (fresh(seen.tokens))
|
|
104
|
+
return answer(seen.tokens);
|
|
105
|
+
}
|
|
106
|
+
const refused = answered ?? remembered(seen.tokens) ?? seen.tokens;
|
|
107
|
+
return answer(await shared(async () => {
|
|
100
108
|
const current = storage.read();
|
|
101
109
|
if (!current)
|
|
102
110
|
throw notSignedIn();
|
|
111
|
+
const latest = remembered(current.tokens) ?? current.tokens;
|
|
103
112
|
// Another process refreshed while this one waited for the lock: its result is the one to use.
|
|
104
|
-
if (
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
113
|
+
if (refresh) {
|
|
114
|
+
if (latest.accessToken !== refused.accessToken && fresh(latest))
|
|
115
|
+
return latest;
|
|
116
|
+
}
|
|
117
|
+
else {
|
|
118
|
+
if (latest !== current.tokens)
|
|
119
|
+
return latest;
|
|
120
|
+
if (fresh(current.tokens) || current.tokens.refreshToken !== seen.tokens.refreshToken)
|
|
121
|
+
return current.tokens;
|
|
122
|
+
}
|
|
109
123
|
const client = clientOf(current);
|
|
110
124
|
const refreshed = await exchange({ grant_type: "refresh_token", refresh_token: current.tokens.refreshToken }, context(client, current.tokens));
|
|
111
125
|
// A rotated refresh token is saved even so: kept only in memory, it would die with the process.
|
|
@@ -114,12 +128,12 @@ export const tokenKeeper = (options) => {
|
|
|
114
128
|
else
|
|
115
129
|
storage.write({ tokens: refreshed, client });
|
|
116
130
|
return refreshed;
|
|
117
|
-
});
|
|
131
|
+
}));
|
|
118
132
|
};
|
|
119
133
|
return {
|
|
120
134
|
tokens,
|
|
121
|
-
accessToken: async () => {
|
|
122
|
-
const current = await tokens();
|
|
135
|
+
accessToken: async (tokensOptions) => {
|
|
136
|
+
const current = await tokens(tokensOptions);
|
|
123
137
|
if (!current.accessToken)
|
|
124
138
|
throw notSignedIn();
|
|
125
139
|
return current.accessToken;
|
|
@@ -93,7 +93,12 @@ export declare const readsOf: (record: PersonRecord, asked: AccountKey) => {
|
|
|
93
93
|
* What the store knows about one person, across every identity linked to them. Reads the store
|
|
94
94
|
* only: nothing is fetched, nothing marked read. Identity is by id, never by name.
|
|
95
95
|
*/
|
|
96
|
-
export declare const personContext: (store: MessageStore, asked: AccountKey, reference: string,
|
|
96
|
+
export declare const personContext: (store: MessageStore, asked: AccountKey, reference: string, options?: ContextOptions) => Promise<PersonContext>;
|
|
97
|
+
/** `personContext` of a person already found, read in the identities and accounts `reads` names. */
|
|
98
|
+
export declare const recordContext: (store: MessageStore, record: PersonRecord, reads: {
|
|
99
|
+
key: AccountKey;
|
|
100
|
+
identity: LinkedIdentity;
|
|
101
|
+
}[], { messages, bytes, since, now }?: ContextOptions) => Promise<Omit<PersonContext, "person">>;
|
|
97
102
|
/** `max:Ana` names a person in another messenger of this store; a reference with no prefix, this one. */
|
|
98
103
|
export declare const identityIn: (store: MessageStore, asked: AccountKey, reference: string) => Promise<IdentityRef>;
|
|
99
104
|
export declare const CHAT_MESSAGES = 20;
|
|
@@ -92,11 +92,18 @@ export const readsOf = (record, asked) => record.identities.flatMap((identity) =
|
|
|
92
92
|
* What the store knows about one person, across every identity linked to them. Reads the store
|
|
93
93
|
* only: nothing is fetched, nothing marked read. Identity is by id, never by name.
|
|
94
94
|
*/
|
|
95
|
-
export const personContext = async (store, asked, reference,
|
|
95
|
+
export const personContext = async (store, asked, reference, options = {}) => {
|
|
96
96
|
const found = pickPerson(reference, await store.people.people(asked.provider, { account: asked.account }));
|
|
97
97
|
const record = await store.people.personOf({ provider: asked.provider, id: found.id });
|
|
98
98
|
if (!record)
|
|
99
99
|
throw new CliError("not_found", `no person ${found.id} in the store`);
|
|
100
|
+
return {
|
|
101
|
+
person: { ...record, provider: asked.provider, id: found.id },
|
|
102
|
+
...(await recordContext(store, record, readsOf(record, asked), options)),
|
|
103
|
+
};
|
|
104
|
+
};
|
|
105
|
+
/** `personContext` of a person already found, read in the identities and accounts `reads` names. */
|
|
106
|
+
export const recordContext = async (store, record, reads, { messages = CONTEXT_MESSAGES, bytes = CONTEXT_BYTES, since, now = Date.now() } = {}) => {
|
|
100
107
|
const recent = (message) => since === undefined || Date.parse(message.timestamp) >= since;
|
|
101
108
|
const shared = [];
|
|
102
109
|
const notRead = [];
|
|
@@ -106,7 +113,7 @@ export const personContext = async (store, asked, reference, { messages = CONTEX
|
|
|
106
113
|
const theirs = [];
|
|
107
114
|
const mentions = [];
|
|
108
115
|
let cut = false;
|
|
109
|
-
for (const { key, identity } of
|
|
116
|
+
for (const { key, identity } of reads) {
|
|
110
117
|
const chats = await store.chats.chatsWith(key, identity.id);
|
|
111
118
|
for (const chat of chats) {
|
|
112
119
|
shared.push({ ...key, chatId: chat.id, title: chat.title, kind: chat.kind, lastMessageAt: chat.lastMessageAt });
|
|
@@ -177,7 +184,6 @@ export const personContext = async (store, asked, reference, { messages = CONTEX
|
|
|
177
184
|
cut ||= direct.length > messages || groups.length > messages || mentions.length > messages;
|
|
178
185
|
const kept = withinBytes(lists, bytes);
|
|
179
186
|
return {
|
|
180
|
-
person: { ...record, provider: asked.provider, id: found.id },
|
|
181
187
|
shared: shared.sort((a, b) => Date.parse(b.lastMessageAt ?? "") - Date.parse(a.lastMessageAt ?? "")),
|
|
182
188
|
last: {
|
|
183
189
|
fromThem: each.find((message) => !message.outgoing) ?? null,
|
|
@@ -165,7 +165,9 @@ export const evidenceFindingsStoreOver = (context) => {
|
|
|
165
165
|
}
|
|
166
166
|
const input = proposal.payload;
|
|
167
167
|
// Rejecting creates nothing, so a batch whose evidence changed can still be closed. Accepting needs only the
|
|
168
|
-
// accepted findings' own
|
|
168
|
+
// accepted findings' own evidence and assignee unchanged. The project is left out: every task a sibling batch
|
|
169
|
+
// creates moves its row, and the project still has to exist, which citing checks. A batch staged before parts
|
|
170
|
+
// were kept needs all.
|
|
169
171
|
if (accepted.length) {
|
|
170
172
|
const parts = input.snapshotParts;
|
|
171
173
|
if (!parts) {
|
|
@@ -175,7 +177,7 @@ export const evidenceFindingsStoreOver = (context) => {
|
|
|
175
177
|
else {
|
|
176
178
|
const chosen = { ...batch.input, findings: batch.input.findings.filter((f) => accepted.includes(f.id)) };
|
|
177
179
|
const now = core.snapshotParts(chosen, cite());
|
|
178
|
-
if (Object.entries(now).some(([key, hash]) => parts[key] !== hash))
|
|
180
|
+
if (Object.entries(now).some(([key, hash]) => !key.startsWith("project:") && parts[key] !== hash))
|
|
179
181
|
return stale();
|
|
180
182
|
}
|
|
181
183
|
}
|
|
@@ -27,6 +27,7 @@ export const PROJECT_TASK_FIELDS = [
|
|
|
27
27
|
relation: "low, medium, high, critical or none; a range of labels or of the 0–1 number: priority:[high TO critical]",
|
|
28
28
|
}),
|
|
29
29
|
field("agent", "kw", "agent:hermes", { relation: "the agent working on it, by name; none or any" }),
|
|
30
|
+
field("parent", "kw", "parent:HRB-1", { relation: "the subtasks of a task, by its key; none or any" }),
|
|
30
31
|
field("due", "time", "due:[today TO 2026-12-31]", {
|
|
31
32
|
relation: "the due date; today, tomorrow and yesterday are your calendar days; NOT due:* has none",
|
|
32
33
|
}),
|
|
@@ -134,6 +135,20 @@ const agentWhere = (node) => {
|
|
|
134
135
|
? { sql: "lower(t.agent) GLOB ?", params: [value] }
|
|
135
136
|
: { sql: "lower(t.agent) = ?", params: [value] };
|
|
136
137
|
};
|
|
138
|
+
/** A key the task had before it moved to another project still names it. */
|
|
139
|
+
const parentWhere = (node) => {
|
|
140
|
+
const value = node.value.trim();
|
|
141
|
+
if (value.toLowerCase() === "none")
|
|
142
|
+
return { sql: "t.parent_id IS NULL", params: [] };
|
|
143
|
+
if (value.toLowerCase() === "any")
|
|
144
|
+
return { sql: "t.parent_id IS NOT NULL", params: [] };
|
|
145
|
+
const key = value.replace(/^task:/, "");
|
|
146
|
+
return {
|
|
147
|
+
sql: "t.parent_id IN (SELECT id FROM tasks WHERE key = ? OR package_id = ? UNION SELECT task_id FROM task_events " +
|
|
148
|
+
"WHERE kind = 'project' AND json_extract(changes, '$.key[0]') = ?)",
|
|
149
|
+
params: [key.toUpperCase(), key, key.toUpperCase()],
|
|
150
|
+
};
|
|
151
|
+
};
|
|
137
152
|
const truth = (node, sql) => ({
|
|
138
153
|
sql: node.value.toLowerCase() === "true" ? sql : `NOT (${sql})`,
|
|
139
154
|
params: [],
|
|
@@ -166,6 +181,7 @@ export const projectTaskColumns = (now, zone) => ({
|
|
|
166
181
|
assignee: { where: assigneeWhere },
|
|
167
182
|
priority: { where: priorityWhere },
|
|
168
183
|
agent: { where: agentWhere },
|
|
184
|
+
parent: { where: parentWhere },
|
|
169
185
|
due: { type: "time", sql: "t.due_at" },
|
|
170
186
|
closed: { type: "time", sql: "t.closed_at" },
|
|
171
187
|
overdue: { where: (node) => truth(node, dueGroups(now, zone).overdue) },
|
|
@@ -31,7 +31,10 @@ export interface TaskFields {
|
|
|
31
31
|
reason?: string | null;
|
|
32
32
|
/** A name the caller checked against its category list; every change is a `category` event. */
|
|
33
33
|
category?: string;
|
|
34
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* Another owner project, by key or `project:<id>`: the task takes the next key there; its old key still finds it.
|
|
36
|
+
* A subtask moves only with `parent: null` in the same edit.
|
|
37
|
+
*/
|
|
35
38
|
project?: string;
|
|
36
39
|
/** A project task's kind, bug, feature or chore. */
|
|
37
40
|
type?: WorkTaskType;
|
|
@@ -170,7 +170,7 @@ export const workTaskOperationsOver = (context) => {
|
|
|
170
170
|
const details = (id) => detailsOf(rowById(id));
|
|
171
171
|
const onBoard = (task) => Boolean(database.prepare(`SELECT 1 FROM tasks t WHERE t.id = ? AND ${BOARD_SCOPE}`).get(task));
|
|
172
172
|
/** The task takes the target's next number and key; the source's counter never goes back, so its old key stays unique. */
|
|
173
|
-
const moveToProject = (task, before, target, actor, at) => {
|
|
173
|
+
const moveToProject = (task, before, target, actor, at, detach) => {
|
|
174
174
|
if (target === Number(before.project_id))
|
|
175
175
|
return;
|
|
176
176
|
if (!onBoard(task))
|
|
@@ -178,6 +178,10 @@ export const workTaskOperationsOver = (context) => {
|
|
|
178
178
|
const targetRow = database.prepare("SELECT key FROM projects WHERE id = ?").get(target);
|
|
179
179
|
if (String(targetRow.key).length === INBOX_KEY_LENGTH)
|
|
180
180
|
throw new CliError("validation_error", "a task moves only into one of your projects");
|
|
181
|
+
if (before.parent_id != null && !detach) {
|
|
182
|
+
const parent = taskIdOf(database, Number(before.parent_id));
|
|
183
|
+
throw new CliError("validation_error", `${String(before.key)} is a subtask of ${parent}; detach it in the same write (parent none) to move it alone`, { reason: "task.is-subtask", parent });
|
|
184
|
+
}
|
|
181
185
|
const children = database
|
|
182
186
|
.prepare("SELECT key FROM tasks WHERE parent_id = ? AND deleted_at IS NULL ORDER BY id")
|
|
183
187
|
.all(task)
|
|
@@ -280,7 +284,7 @@ export const workTaskOperationsOver = (context) => {
|
|
|
280
284
|
bump = true;
|
|
281
285
|
}
|
|
282
286
|
if (fields.project !== undefined)
|
|
283
|
-
moveToProject(task, before, projectOf(fields.project), actor, at);
|
|
287
|
+
moveToProject(task, before, projectOf(fields.project), actor, at, fields.parent === null);
|
|
284
288
|
if (set.length === 0) {
|
|
285
289
|
if (bump)
|
|
286
290
|
database.prepare("UPDATE tasks SET revision = revision + 1 WHERE id = ?").run(task);
|