@joe-broadhead/messaging-bin-win32-x64 0.8.0 → 0.9.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/bin/messagingd.exe +2 -2
- package/lib/messagingd.mjs +1012 -131
- package/package.json +1 -1
package/lib/messagingd.mjs
CHANGED
|
@@ -6376,7 +6376,8 @@ var init_common = __esm(() => {
|
|
|
6376
6376
|
URI = {
|
|
6377
6377
|
binding: "urn:messaging:binding:relay:v1",
|
|
6378
6378
|
provenance: "urn:messaging:ext:provenance:v1",
|
|
6379
|
-
presence: "urn:messaging:ext:presence:v1"
|
|
6379
|
+
presence: "urn:messaging:ext:presence:v1",
|
|
6380
|
+
signIn: "urn:messaging:ext:sign-in:v1"
|
|
6380
6381
|
};
|
|
6381
6382
|
Base64Url = string2().regex(/^[A-Za-z0-9_-]*$/, "base64url without padding");
|
|
6382
6383
|
Key32 = string2().regex(/^[A-Za-z0-9_-]{43}$/, "32 bytes, base64url without padding");
|
|
@@ -6550,6 +6551,7 @@ var init_errors3 = __esm(() => {
|
|
|
6550
6551
|
"jelly_unavailable",
|
|
6551
6552
|
"log_conflict",
|
|
6552
6553
|
"conv_conflict",
|
|
6554
|
+
"conflict",
|
|
6553
6555
|
"stale_epoch",
|
|
6554
6556
|
"device_limit",
|
|
6555
6557
|
"admission_required",
|
|
@@ -6803,7 +6805,7 @@ var init_directory = __esm(() => {
|
|
|
6803
6805
|
});
|
|
6804
6806
|
|
|
6805
6807
|
// packages/core/dist/schema/a2a.js
|
|
6806
|
-
var Struct, TaskState, TERMINAL_STATES, Role, Part, Message, TaskStatus, Artifact, Task, TaskStatusUpdateEvent, TaskArtifactUpdateEvent;
|
|
6808
|
+
var Struct, TaskState, TERMINAL_STATES, INTERRUPTED_STATES, Role, Part, Message, TaskStatus, Artifact, Task, TaskStatusUpdateEvent, TaskArtifactUpdateEvent;
|
|
6807
6809
|
var init_a2a = __esm(() => {
|
|
6808
6810
|
init_zod();
|
|
6809
6811
|
Struct = record(string2(), unknown());
|
|
@@ -6823,6 +6825,7 @@ var init_a2a = __esm(() => {
|
|
|
6823
6825
|
"TASK_STATE_CANCELED",
|
|
6824
6826
|
"TASK_STATE_REJECTED"
|
|
6825
6827
|
];
|
|
6828
|
+
INTERRUPTED_STATES = ["TASK_STATE_INPUT_REQUIRED", "TASK_STATE_AUTH_REQUIRED"];
|
|
6826
6829
|
Role = _enum(["ROLE_USER", "ROLE_AGENT"]);
|
|
6827
6830
|
Part = object({
|
|
6828
6831
|
text: string2().optional(),
|
|
@@ -7173,6 +7176,16 @@ function grantProblem(grant) {
|
|
|
7173
7176
|
if ("conv" in grant.subject && (grant.chat !== undefined || grant.trustedUntil !== undefined || grant.discoverable !== undefined)) {
|
|
7174
7177
|
return "a conversation grant sets only ask and agentMail";
|
|
7175
7178
|
}
|
|
7179
|
+
if ("relation" in grant.subject) {
|
|
7180
|
+
if (grant.agentMail !== undefined || grant.trustedUntil !== undefined || grant.discoverable !== undefined)
|
|
7181
|
+
return "a relation grant sets only chat or ask";
|
|
7182
|
+
if (grant.subject.relation === "outside" && grant.ask !== undefined)
|
|
7183
|
+
return "the outside relation grant sets only chat";
|
|
7184
|
+
if (grant.subject.relation === "outside" && grant.chat === "muted")
|
|
7185
|
+
return "the outside relation grant sets chat open or blocked";
|
|
7186
|
+
if (grant.subject.relation === "org" && grant.chat !== undefined)
|
|
7187
|
+
return "the org relation grant sets only ask";
|
|
7188
|
+
}
|
|
7176
7189
|
if (own && (grant.chat !== undefined || grant.agentMail !== undefined || grant.ask !== undefined || grant.trustedUntil !== undefined)) {
|
|
7177
7190
|
return "a grant about its own person sets only discoverable";
|
|
7178
7191
|
}
|
|
@@ -7195,7 +7208,7 @@ function cardProblem(card) {
|
|
|
7195
7208
|
return "trustedUntil needs ask open";
|
|
7196
7209
|
return;
|
|
7197
7210
|
}
|
|
7198
|
-
var ChatLevel, AgentMailLevel, AskLevel, GrantSubject, ConsentGrant, GRANTS_PER_SYNC = 100, GrantSync, CardCan, ContactCard, CardData, CardGetData;
|
|
7211
|
+
var ChatLevel, AgentMailLevel, AskLevel, GrantRelation, GrantSubject, ConsentGrant, GRANTS_PER_SYNC = 100, GrantSync, CardCan, ContactCard, CardData, CardGetData;
|
|
7199
7212
|
var init_consent = __esm(() => {
|
|
7200
7213
|
init_zod();
|
|
7201
7214
|
init_common();
|
|
@@ -7204,7 +7217,8 @@ var init_consent = __esm(() => {
|
|
|
7204
7217
|
ChatLevel = _enum(["open", "muted", "blocked"]);
|
|
7205
7218
|
AgentMailLevel = _enum(["open", "blocked"]);
|
|
7206
7219
|
AskLevel = _enum(["open", "request", "no"]);
|
|
7207
|
-
|
|
7220
|
+
GrantRelation = _enum(["outside", "org"]);
|
|
7221
|
+
GrantSubject = union([strictObject({ person: PersonId }), strictObject({ conv: ConvId }), strictObject({ relation: GrantRelation })]);
|
|
7208
7222
|
ConsentGrant = object({
|
|
7209
7223
|
v: literal(PROTOCOL),
|
|
7210
7224
|
realm: RealmId,
|
|
@@ -7554,13 +7568,14 @@ function hostedRelayAuthPayload(args) {
|
|
|
7554
7568
|
function dirReadPayload(args) {
|
|
7555
7569
|
return { proto: RELAY_PROTOCOL, relay: args.relay, realm: args.realm, device: args.device, nonce: args.nonce };
|
|
7556
7570
|
}
|
|
7557
|
-
var Req3, Size, Handle, Email, OrgId, DirReadProof, DirBindFrame, HostedHelloFrame, DirAppendFrame, DirKeysFrame, DirSthFrame, DirLookupFrame, DirHeadsFrame, DirDeviceFrame, DirResolveFrame, DirSearchFrame, DirWatchFrame, PresenceWatchFrame, DirEraseFrame, WAKE_AGENTS = 64, WAKE_AWAITING = 500, WAKE_TICKET_BYTES = 4096, WAKE_TICKET_MAX_S, WakeUrl, WakeArmFrame, WakeDisarmFrame, DirectoryClientFrame, HostedWelcomeFrame, DirOkFrame, DirSthIsFrame, DirPersonsFrame, DirHeadsAreFrame, ResolvedPerson, DirResolvedFrame, DirChangedFrame, DirUnboundFrame, DirectoryRelayFrame;
|
|
7571
|
+
var Req3, Size, Handle, Email, OrgId, DirReadProof, DirBindFrame, HostedHelloFrame, DirAppendFrame, DirKeysFrame, DirSthFrame, DirLookupFrame, DirHeadsFrame, DirDeviceFrame, DirResolveFrame, DirSearchFrame, DirWatchFrame, PresenceWatchFrame, DirEraseFrame, WAKE_AGENTS = 64, WAKE_AWAITING = 500, WAKE_DMS = 256, WAKE_TICKET_BYTES = 4096, WAKE_TICKET_MAX_S, WakeUrl, WakeArmFrame, WakeDisarmFrame, DirectoryClientFrame, HostedWelcomeFrame, DirOkFrame, DirSthIsFrame, DirPersonsFrame, DirHeadsAreFrame, ResolvedPerson, DirResolvedFrame, DirChangedFrame, DirUnboundFrame, DirectoryRelayFrame;
|
|
7558
7572
|
var init_directory_frames = __esm(() => {
|
|
7559
7573
|
init_zod();
|
|
7560
7574
|
init_common();
|
|
7561
7575
|
init_directory();
|
|
7562
7576
|
init_realm();
|
|
7563
7577
|
init_directory();
|
|
7578
|
+
init_conversation();
|
|
7564
7579
|
init_jelly();
|
|
7565
7580
|
Req3 = string2().min(1).max(64);
|
|
7566
7581
|
Size = number2().int().positive();
|
|
@@ -7635,7 +7650,8 @@ var init_directory_frames = __esm(() => {
|
|
|
7635
7650
|
url: WakeUrl,
|
|
7636
7651
|
ticket: Jws.refine((ticket) => ticket.length <= WAKE_TICKET_BYTES, { message: `at most ${WAKE_TICKET_BYTES} bytes` }),
|
|
7637
7652
|
agents: array(SessionName).max(WAKE_AGENTS),
|
|
7638
|
-
awaiting: array(Ulid).max(WAKE_AWAITING)
|
|
7653
|
+
awaiting: array(Ulid).max(WAKE_AWAITING),
|
|
7654
|
+
dms: array(ConvId).max(WAKE_DMS).optional()
|
|
7639
7655
|
});
|
|
7640
7656
|
WakeDisarmFrame = object({ t: literal("wake.disarm"), req: Req3 });
|
|
7641
7657
|
DirectoryClientFrame = discriminatedUnion("t", [
|
|
@@ -7717,7 +7733,7 @@ var init_directory_frames = __esm(() => {
|
|
|
7717
7733
|
});
|
|
7718
7734
|
|
|
7719
7735
|
// packages/core/dist/schema/ipc/device.js
|
|
7720
|
-
var BindingState, RealmAlarmSummary, DeviceStatus, DeviceChangeView, DeviceListEntry, LinkText, Sas, LinkPreview, KeyFingerprint, LoginUserCode, LoginPurpose, LoginKind, LoginShown, LoginState, DeviceRemoveParams, DeviceApproveParams, IdentityReplaceParams, SignInStep, DirectoryEraseParams, DEVICE_METHODS;
|
|
7736
|
+
var BindingState, RealmAlarmSummary, DeviceStatus, DeviceChangeView, PresenceFeature, DeviceListEntry, LinkText, Sas, LinkPreview, KeyFingerprint, LoginUserCode, LoginPurpose, LoginKind, LoginShown, LoginState, DeviceRemoveParams, DeviceApproveParams, IdentityReplaceParams, SignInStep, DirectoryEraseParams, DEVICE_METHODS;
|
|
7721
7737
|
var init_device = __esm(() => {
|
|
7722
7738
|
init_zod();
|
|
7723
7739
|
init_common();
|
|
@@ -7749,6 +7765,7 @@ var init_device = __esm(() => {
|
|
|
7749
7765
|
approver: MachineLabel.optional(),
|
|
7750
7766
|
at: Millis
|
|
7751
7767
|
});
|
|
7768
|
+
PresenceFeature = string2().regex(/^[a-z][a-z0-9-]{0,31}$/, "a feature name");
|
|
7752
7769
|
DeviceListEntry = object({
|
|
7753
7770
|
device: MachineId,
|
|
7754
7771
|
label: MachineLabel,
|
|
@@ -7759,7 +7776,8 @@ var init_device = __esm(() => {
|
|
|
7759
7776
|
self: boolean2(),
|
|
7760
7777
|
away: literal(true).optional(),
|
|
7761
7778
|
since: Millis.optional(),
|
|
7762
|
-
unbound: object({ at: Millis, reason: UnbindReason, by: MachineId }).optional()
|
|
7779
|
+
unbound: object({ at: Millis, reason: UnbindReason, by: MachineId }).optional(),
|
|
7780
|
+
features: array(PresenceFeature).max(32).optional()
|
|
7763
7781
|
});
|
|
7764
7782
|
LinkText = string2().min(1).max(2048);
|
|
7765
7783
|
Sas = string2().regex(/^[0-9]{6}$/, "six digits");
|
|
@@ -7866,7 +7884,10 @@ var init_device = __esm(() => {
|
|
|
7866
7884
|
});
|
|
7867
7885
|
|
|
7868
7886
|
// packages/core/dist/schema/ipc/shapes.js
|
|
7869
|
-
|
|
7887
|
+
function taskViewRules(schema) {
|
|
7888
|
+
return schema.refine((r) => r.signIn === undefined || r.task.status.state === "TASK_STATE_AUTH_REQUIRED", "signIn is for an AUTH_REQUIRED task").refine((r) => r.refusal === undefined || r.task.status.state === "TASK_STATE_REJECTED", "refusal is for a REJECTED task").refine((r) => r.question === undefined || r.role === "executor" && (r.task.status.state === "TASK_STATE_AUTH_REQUIRED" || r.task.status.state === "TASK_STATE_INPUT_REQUIRED"), "question is for the executor's side of an AUTH_REQUIRED or INPUT_REQUIRED task");
|
|
7889
|
+
}
|
|
7890
|
+
var SessionKey, ActionToken, SendExpect, DirectoryEntry, PeerRef, InboxItem, TaskFollowUp, TASK_SIGN_IN_TOOL_CHARS, TASK_REFUSAL_CHARS, HostLocalId, TaskQuestion, TaskRecordFields, TaskRecord, PersonTask, SendTarget, SendResult;
|
|
7870
7891
|
var init_shapes = __esm(() => {
|
|
7871
7892
|
init_zod();
|
|
7872
7893
|
init_a2a();
|
|
@@ -7875,6 +7896,7 @@ var init_shapes = __esm(() => {
|
|
|
7875
7896
|
init_delegation();
|
|
7876
7897
|
init_errors3();
|
|
7877
7898
|
SessionKey = string2().min(1).max(200);
|
|
7899
|
+
ActionToken = string2().regex(/^[A-Za-z0-9_-]{43}$/, "action token");
|
|
7878
7900
|
SendExpect = _enum(["next_turn", "wake", "next_prompt", "on_read", "kept", "offline"]);
|
|
7879
7901
|
DirectoryEntry = object({
|
|
7880
7902
|
address: SessionAddress,
|
|
@@ -7926,7 +7948,11 @@ var init_shapes = __esm(() => {
|
|
|
7926
7948
|
stage: DeliveryStage,
|
|
7927
7949
|
at: Millis
|
|
7928
7950
|
});
|
|
7929
|
-
|
|
7951
|
+
TASK_SIGN_IN_TOOL_CHARS = LIMITS.previewChars;
|
|
7952
|
+
TASK_REFUSAL_CHARS = LIMITS.requestChars;
|
|
7953
|
+
HostLocalId = string2().regex(/^[A-Za-z0-9][A-Za-z0-9._:-]{0,199}$/, "a plain identifier of at most 200 characters");
|
|
7954
|
+
TaskQuestion = strictObject({ sessionId: HostLocalId, approvalId: HostLocalId });
|
|
7955
|
+
TaskRecordFields = object({
|
|
7930
7956
|
task: Task.extend({ id: Ulid }),
|
|
7931
7957
|
role: _enum(["requester", "executor"]),
|
|
7932
7958
|
key: SessionKey.describe("local session that owns this side of the task"),
|
|
@@ -7934,8 +7960,20 @@ var init_shapes = __esm(() => {
|
|
|
7934
7960
|
createdAt: Millis,
|
|
7935
7961
|
updatedAt: Millis,
|
|
7936
7962
|
chain: DelegationSummary.optional(),
|
|
7937
|
-
followUps: array(TaskFollowUp).min(1).max(LIMITS.taskFollowUps).optional()
|
|
7938
|
-
|
|
7963
|
+
followUps: array(TaskFollowUp).min(1).max(LIMITS.taskFollowUps).optional(),
|
|
7964
|
+
agent: SessionName.optional(),
|
|
7965
|
+
person: object({ id: PersonId, name: string2().max(200) }).optional(),
|
|
7966
|
+
org: _enum(["same", "other", "none"]).optional(),
|
|
7967
|
+
signIn: object({ tool: string2().min(1).max(TASK_SIGN_IN_TOOL_CHARS).optional(), by: _enum(["you", "them"]) }).optional(),
|
|
7968
|
+
refusal: object({ reason: string2().min(1).max(TASK_REFUSAL_CHARS) }).optional(),
|
|
7969
|
+
question: TaskQuestion.optional()
|
|
7970
|
+
});
|
|
7971
|
+
TaskRecord = taskViewRules(TaskRecordFields);
|
|
7972
|
+
PersonTask = taskViewRules(TaskRecordFields.omit({ key: true }).extend({
|
|
7973
|
+
key: never().optional(),
|
|
7974
|
+
session: SessionName.describe("this person's agent on this device that holds this side of the task"),
|
|
7975
|
+
cancel: strictObject({ name: literal("cancel"), token: ActionToken }).optional()
|
|
7976
|
+
})).refine((r) => r.cancel === undefined || r.role === "requester", "cancel is for the requester side");
|
|
7939
7977
|
SendTarget = SessionAddress;
|
|
7940
7978
|
SendResult = object({
|
|
7941
7979
|
to: SendTarget,
|
|
@@ -7950,13 +7988,14 @@ var init_shapes = __esm(() => {
|
|
|
7950
7988
|
});
|
|
7951
7989
|
|
|
7952
7990
|
// packages/core/dist/schema/ipc/consent.js
|
|
7953
|
-
var GrantTarget, Levels, GrantView, GrantReview, GrantSetParams, AskRequest, ContactRequestView, AgentInvitationRequest, ContactSuggestion, HeldRequest,
|
|
7991
|
+
var GrantTarget, Levels, GrantView, GrantReview, GrantSetParams, RequestAnswer, RequestAction, Actions, AskRequest, ContactRequestView, AgentInvitationRequest, ContactSuggestion, HeldRequest, RequestText, PersonReach, RequestAnswerParams, ContactRequestParams, ReachGroupId, ReachValue, ReachOption, ReachGroup, ReachSnapshot, ReachSetParams, REACH_QUESTIONS, CONSENT_METHODS;
|
|
7954
7992
|
var init_consent2 = __esm(() => {
|
|
7955
7993
|
init_zod();
|
|
7956
7994
|
init_common();
|
|
7957
7995
|
init_conversation();
|
|
7958
7996
|
init_consent();
|
|
7959
7997
|
init_directory_frames();
|
|
7998
|
+
init_shapes();
|
|
7960
7999
|
GrantTarget = union([strictObject({ person: PersonId }), strictObject({ conv: ConvId })]);
|
|
7961
8000
|
Levels = {
|
|
7962
8001
|
chat: ChatLevel.optional(),
|
|
@@ -7983,6 +8022,9 @@ var init_consent2 = __esm(() => {
|
|
|
7983
8022
|
at: Millis
|
|
7984
8023
|
});
|
|
7985
8024
|
GrantSetParams = strictObject({ subject: GrantTarget, ...Levels, trust: literal(true).optional() });
|
|
8025
|
+
RequestAnswer = _enum(["once", "always", "accept", "decline", "block"]);
|
|
8026
|
+
RequestAction = strictObject({ name: RequestAnswer, token: ActionToken });
|
|
8027
|
+
Actions = { actions: array(RequestAction).max(4).optional() };
|
|
7986
8028
|
AskRequest = object({
|
|
7987
8029
|
id: Ulid,
|
|
7988
8030
|
kind: _enum(["message", "task"]),
|
|
@@ -7990,7 +8032,8 @@ var init_consent2 = __esm(() => {
|
|
|
7990
8032
|
to: SessionName,
|
|
7991
8033
|
preview: string2().max(LIMITS.previewChars),
|
|
7992
8034
|
at: Millis,
|
|
7993
|
-
exp: Millis
|
|
8035
|
+
exp: Millis,
|
|
8036
|
+
...Actions
|
|
7994
8037
|
});
|
|
7995
8038
|
ContactRequestView = object({
|
|
7996
8039
|
id: Ulid,
|
|
@@ -8007,23 +8050,62 @@ var init_consent2 = __esm(() => {
|
|
|
8007
8050
|
}),
|
|
8008
8051
|
text: string2().max(LIMITS.requestChars),
|
|
8009
8052
|
at: Millis,
|
|
8010
|
-
exp: Millis
|
|
8053
|
+
exp: Millis,
|
|
8054
|
+
...Actions
|
|
8011
8055
|
});
|
|
8012
|
-
AgentInvitationRequest = strictObject({ kind: literal("agent-invitation"), id: Ulid, cid: ConvId, from: ParticipantId, to: ParticipantId, at: Millis });
|
|
8056
|
+
AgentInvitationRequest = strictObject({ kind: literal("agent-invitation"), id: Ulid, cid: ConvId, from: ParticipantId, to: ParticipantId, at: Millis, ...Actions });
|
|
8013
8057
|
ContactSuggestion = object({
|
|
8014
8058
|
id: Ulid,
|
|
8015
8059
|
kind: literal("suggestion"),
|
|
8016
8060
|
agent: SessionName,
|
|
8017
8061
|
email: Email,
|
|
8018
|
-
at: Millis
|
|
8062
|
+
at: Millis,
|
|
8063
|
+
...Actions
|
|
8019
8064
|
});
|
|
8020
8065
|
HeldRequest = union([AskRequest, ContactRequestView, ContactSuggestion, AgentInvitationRequest]);
|
|
8021
|
-
RequestAnswer = _enum(["once", "always", "accept", "decline", "block"]);
|
|
8022
8066
|
RequestText = string2().min(1).refine((text) => text.length <= LIMITS.requestChars, { message: `at most ${LIMITS.requestChars} UTF-16 code units` });
|
|
8023
8067
|
PersonReach = object({ person: PersonId, can: CardCan, source: _enum(["card", "default"]), at: Millis.optional(), devicesChangedAt: Millis.optional() });
|
|
8024
|
-
RequestAnswerParams = strictObject({ id: Ulid, answer: RequestAnswer, all: boolean2().optional() });
|
|
8068
|
+
RequestAnswerParams = strictObject({ id: Ulid, answer: RequestAnswer, all: boolean2().optional(), token: ActionToken.optional() }).refine((p) => p.token === undefined || p.all !== true, "an action token answers one request: drop all");
|
|
8025
8069
|
ContactRequestParams = strictObject({ email: Email, text: RequestText, suggestion: Ulid.optional() });
|
|
8070
|
+
ReachGroupId = _enum(["message", "ask"]);
|
|
8071
|
+
ReachValue = _enum(["open", "ask", "never"]);
|
|
8072
|
+
ReachOption = object({ value: ReachValue, label: string2().max(40), description: string2().max(300) });
|
|
8073
|
+
ReachGroup = object({
|
|
8074
|
+
id: ReachGroupId,
|
|
8075
|
+
label: string2().max(80),
|
|
8076
|
+
options: array(ReachOption).length(3),
|
|
8077
|
+
value: ReachValue.nullable(),
|
|
8078
|
+
lapsed: ReachValue.optional()
|
|
8079
|
+
});
|
|
8080
|
+
ReachSnapshot = object({ revision: string2().min(1).max(64), available: boolean2(), groups: array(ReachGroup).length(2) });
|
|
8081
|
+
ReachSetParams = strictObject({
|
|
8082
|
+
revision: string2().min(1).max(64),
|
|
8083
|
+
changes: strictObject({ message: ReachValue.optional(), ask: ReachValue.optional() })
|
|
8084
|
+
});
|
|
8085
|
+
REACH_QUESTIONS = [
|
|
8086
|
+
{
|
|
8087
|
+
id: "message",
|
|
8088
|
+
label: "Who can message you",
|
|
8089
|
+
options: [
|
|
8090
|
+
{ value: "open", label: "Open", description: "Anyone outside your org may message you." },
|
|
8091
|
+
{ value: "ask", label: "Ask me first", description: "People outside your org send a message request first, and you accept or decline it. People you have accepted may message you." },
|
|
8092
|
+
{ value: "never", label: "Never", description: "No one outside your org may message you or send you a request. Your org, and people you have let message you, still can." }
|
|
8093
|
+
]
|
|
8094
|
+
},
|
|
8095
|
+
{
|
|
8096
|
+
id: "ask",
|
|
8097
|
+
label: "How agents may ask yours",
|
|
8098
|
+
options: [
|
|
8099
|
+
{ value: "open", label: "Open", description: "Your teammates' agents may ask your agents without asking you. Their requests run guarded." },
|
|
8100
|
+
{ value: "ask", label: "Ask me first", description: "Each request from a teammate's agent to yours waits for your answer." },
|
|
8101
|
+
{ value: "never", label: "Never", description: "Your teammates' agents may not ask yours. Agents of people outside your org never may." }
|
|
8102
|
+
]
|
|
8103
|
+
}
|
|
8104
|
+
];
|
|
8026
8105
|
CONSENT_METHODS = {
|
|
8106
|
+
"reach.get": { params: strictObject({}), result: ReachSnapshot },
|
|
8107
|
+
"reach.set": { params: ReachSetParams, result: ReachSnapshot },
|
|
8108
|
+
"reach.set.preview": { params: ReachSetParams, result: object({ sentence: string2().max(500) }) },
|
|
8027
8109
|
"grant.list": {
|
|
8028
8110
|
params: strictObject({ subject: GrantTarget.optional() }),
|
|
8029
8111
|
result: object({ grants: array(GrantView), review: array(GrantReview) })
|
|
@@ -8124,6 +8206,7 @@ var init_jelly2 = __esm(() => {
|
|
|
8124
8206
|
from: object({ name: string2().max(200), email: string2().max(320).optional(), org: _enum(["same", "other", "none"]) }).optional(),
|
|
8125
8207
|
line: string2().max(NOTICE_LINE_CHARS).optional(),
|
|
8126
8208
|
device: NoticeDevice.optional(),
|
|
8209
|
+
key: string2().regex(/^task:[0-9A-HJKMNP-TV-Z]{26}$/, "task:<task id>").optional(),
|
|
8127
8210
|
at: Millis
|
|
8128
8211
|
});
|
|
8129
8212
|
NoticeCounts = object({ requests: number2().int().nonnegative() });
|
|
@@ -8155,7 +8238,7 @@ var init_jelly2 = __esm(() => {
|
|
|
8155
8238
|
ticket: Jws.refine((ticket) => ticket.length <= WAKE_TICKET_BYTES, { message: `at most ${WAKE_TICKET_BYTES} bytes` }),
|
|
8156
8239
|
agents: array(SessionName).max(WAKE_AGENTS)
|
|
8157
8240
|
}),
|
|
8158
|
-
result: object({ relay: _enum(["armed", "pending"]), awaiting: number2().int().nonnegative() })
|
|
8241
|
+
result: object({ relay: _enum(["armed", "pending"]), awaiting: number2().int().nonnegative(), dms: number2().int().nonnegative() })
|
|
8159
8242
|
},
|
|
8160
8243
|
"wake.disarm": { params: strictObject({}), result: object({ relay: _enum(["disarmed", "pending"]) }) },
|
|
8161
8244
|
"daemon.snapshot": {
|
|
@@ -8192,6 +8275,7 @@ var init_conversation_views = __esm(() => {
|
|
|
8192
8275
|
init_common();
|
|
8193
8276
|
init_conversation();
|
|
8194
8277
|
init_a2a();
|
|
8278
|
+
init_shapes();
|
|
8195
8279
|
Count = number2().int().nonnegative().safe();
|
|
8196
8280
|
Seq2 = number2().int().nonnegative().safe();
|
|
8197
8281
|
Name = string2().max(200);
|
|
@@ -8219,7 +8303,7 @@ var init_conversation_views = __esm(() => {
|
|
|
8219
8303
|
mime: string2().max(100),
|
|
8220
8304
|
bytes: number2().int().positive().max(LIMITS.blobBytes)
|
|
8221
8305
|
});
|
|
8222
|
-
TaskView = object({ id: Ulid, state: TaskState });
|
|
8306
|
+
TaskView = object({ id: Ulid, state: TaskState, cancel: strictObject({ name: literal("cancel"), token: ActionToken }).optional() });
|
|
8223
8307
|
EventView = object({
|
|
8224
8308
|
cid: ConvId,
|
|
8225
8309
|
id: Ulid,
|
|
@@ -8299,7 +8383,8 @@ var init_conversation_views = __esm(() => {
|
|
|
8299
8383
|
object({ type: literal("read"), pos: Seq2, cid: ConvId, readUpTo: Seq2, unread: Count, mentions: Count }),
|
|
8300
8384
|
object({ type: literal("typing"), cid: ConvId, p: ParticipantId, thread: Ulid.optional(), until: Millis }),
|
|
8301
8385
|
object({ type: literal("activity"), body: ActivityBody }),
|
|
8302
|
-
object({ type: literal("gap"), pos: Seq2 })
|
|
8386
|
+
object({ type: literal("gap"), pos: Seq2 }),
|
|
8387
|
+
object({ type: literal("task"), pos: Seq2, task: PersonTask })
|
|
8303
8388
|
]).refine((entry) => entry.type !== "event.update" || entry.cid === entry.event.cid && entry.id === entry.event.id, {
|
|
8304
8389
|
message: "event.update names the same conversation and event as its projection"
|
|
8305
8390
|
});
|
|
@@ -8356,7 +8441,7 @@ var init_conversations = __esm(() => {
|
|
|
8356
8441
|
at: Millis,
|
|
8357
8442
|
cid: ConvId.optional(),
|
|
8358
8443
|
agent: SessionName.optional(),
|
|
8359
|
-
state: _enum(["held", "pending-delivery"])
|
|
8444
|
+
state: _enum(["held", "pending-delivery", "resend", "outdated"])
|
|
8360
8445
|
});
|
|
8361
8446
|
ConvListParams = strictObject({ key: Key, filter: strictObject({ unread: boolean2().optional(), mentions: boolean2().optional(), kind: ConvKind.optional(), archived: boolean2().optional() }).optional(), ...Page });
|
|
8362
8447
|
ConvListResult = ConvPage;
|
|
@@ -8671,12 +8756,35 @@ var init_ipc = __esm(() => {
|
|
|
8671
8756
|
message: strictObject({ parts: array(Part).min(1).max(LIMITS.partsPerMessage) }).optional(),
|
|
8672
8757
|
artifact: Artifact.optional(),
|
|
8673
8758
|
append: boolean2().optional(),
|
|
8674
|
-
lastChunk: boolean2().optional()
|
|
8675
|
-
|
|
8759
|
+
lastChunk: boolean2().optional(),
|
|
8760
|
+
signIn: strictObject({ tool: string2().min(1).max(TASK_SIGN_IN_TOOL_CHARS) }).optional()
|
|
8761
|
+
}).refine((p) => p.signIn === undefined || p.state === "TASK_STATE_AUTH_REQUIRED", "signIn goes with TASK_STATE_AUTH_REQUIRED"),
|
|
8676
8762
|
result: TaskRecord
|
|
8677
8763
|
},
|
|
8764
|
+
"task.question": {
|
|
8765
|
+
params: strictObject({ key: SessionKey, taskId: Ulid, question: TaskQuestion.nullable() }),
|
|
8766
|
+
result: object({ ok: literal(true) })
|
|
8767
|
+
},
|
|
8768
|
+
"tasks.list": {
|
|
8769
|
+
params: strictObject({
|
|
8770
|
+
role: _enum(["requester", "executor"]).optional(),
|
|
8771
|
+
state: TaskState.optional(),
|
|
8772
|
+
session: SessionName.optional().describe("only this agent's tasks"),
|
|
8773
|
+
cursor: string2().min(1).max(200).optional().describe("opaque; from a previous nextCursor"),
|
|
8774
|
+
limit: number2().int().min(1).max(100).optional()
|
|
8775
|
+
}),
|
|
8776
|
+
result: object({
|
|
8777
|
+
tasks: array(PersonTask),
|
|
8778
|
+
nextCursor: string2().max(200).optional().describe("absent when there are no more pages"),
|
|
8779
|
+
total: number2().int().nonnegative()
|
|
8780
|
+
})
|
|
8781
|
+
},
|
|
8782
|
+
"tasks.get": {
|
|
8783
|
+
params: strictObject({ taskId: Ulid, role: _enum(["requester", "executor"]).optional() }),
|
|
8784
|
+
result: PersonTask
|
|
8785
|
+
},
|
|
8678
8786
|
"task.cancel": {
|
|
8679
|
-
params: strictObject({ key: SessionKey, taskId: Ulid, reason: string2().max(1000).optional() }),
|
|
8787
|
+
params: strictObject({ key: SessionKey.optional(), taskId: Ulid, reason: string2().max(1000).optional(), token: ActionToken.optional() }).refine((p) => p.key === undefined !== (p.token === undefined), "give exactly one of key or token"),
|
|
8680
8788
|
result: TaskRecord
|
|
8681
8789
|
},
|
|
8682
8790
|
"events.subscribe": { params: strictObject({ keys: array(SessionKey).min(1).max(256) }), result: Ok2 },
|
|
@@ -8764,7 +8872,8 @@ var init_ipc = __esm(() => {
|
|
|
8764
8872
|
"task.list",
|
|
8765
8873
|
"task.get",
|
|
8766
8874
|
"task.update",
|
|
8767
|
-
"task.cancel"
|
|
8875
|
+
"task.cancel",
|
|
8876
|
+
"task.question"
|
|
8768
8877
|
];
|
|
8769
8878
|
HUMAN_METHODS = [
|
|
8770
8879
|
...conversationTier("human"),
|
|
@@ -8773,6 +8882,9 @@ var init_ipc = __esm(() => {
|
|
|
8773
8882
|
"grant.list",
|
|
8774
8883
|
"grant.set",
|
|
8775
8884
|
"grant.set.preview",
|
|
8885
|
+
"reach.get",
|
|
8886
|
+
"reach.set",
|
|
8887
|
+
"reach.set.preview",
|
|
8776
8888
|
"request.list",
|
|
8777
8889
|
"request.answer",
|
|
8778
8890
|
"request.answer.preview",
|
|
@@ -8782,6 +8894,8 @@ var init_ipc = __esm(() => {
|
|
|
8782
8894
|
"notice.subscribe",
|
|
8783
8895
|
"notice.list",
|
|
8784
8896
|
"notice.ack",
|
|
8897
|
+
"tasks.list",
|
|
8898
|
+
"tasks.get",
|
|
8785
8899
|
"device.approve.preview",
|
|
8786
8900
|
"device.remove.preview",
|
|
8787
8901
|
"identity.replace.preview",
|
|
@@ -8880,7 +8994,12 @@ var init_conv_frames = __esm(() => {
|
|
|
8880
8994
|
});
|
|
8881
8995
|
ConvHeadView = strictObject({ cid: ConvId, head: ConvHead, opHead: ConvHead, shares: Count3 });
|
|
8882
8996
|
ConvHeadsFrame = strictObject({ t: literal("conv.heads"), req: Req4, heads: array(ConvHeadView).max(LIMITS.convHeads), more: boolean2() });
|
|
8883
|
-
ConvCapabilitiesFrame = strictObject({
|
|
8997
|
+
ConvCapabilitiesFrame = strictObject({
|
|
8998
|
+
t: literal("conv.capabilities"),
|
|
8999
|
+
req: Req4,
|
|
9000
|
+
outdated: array(PersonId).max(LIMITS.convOpParticipants),
|
|
9001
|
+
unknown: array(PersonId).min(1).max(LIMITS.convOpParticipants).optional()
|
|
9002
|
+
});
|
|
8884
9003
|
ConvPushFrame = union([
|
|
8885
9004
|
strictObject({ t: literal("conv.push"), cid: ConvId, entry: ConvLogEntry }),
|
|
8886
9005
|
strictObject({ t: literal("conv.push"), cid: ConvId, share: ConvShareEntry })
|
|
@@ -9028,6 +9147,11 @@ var init_agent_tools = __esm(() => {
|
|
|
9028
9147
|
titleAuthor: Author.optional().describe("With title: who wrote the request; ours when you asked (mine)"),
|
|
9029
9148
|
status: authored("The latest status message").optional(),
|
|
9030
9149
|
statusAuthor: Author.optional().describe("With status: who wrote it; ours when you run the task (asked)"),
|
|
9150
|
+
signIn: object({
|
|
9151
|
+
tool: authored("The tool the executor named").optional(),
|
|
9152
|
+
by: _enum(["you", "them"]).describe("Whose sign-in it is: you (your person, for an agent of theirs) or them (the other person); your Messaging daemon decides, never the peer")
|
|
9153
|
+
}).optional().describe("auth_required only: the task waits on a sign-in; the tool was written by the executor, as statusAuthor says"),
|
|
9154
|
+
refusal: object({ reason: authored("The executor's reason") }).optional().describe("rejected only: why the executor declined, in its words, as statusAuthor says"),
|
|
9031
9155
|
createdAt: iso,
|
|
9032
9156
|
updatedAt: iso,
|
|
9033
9157
|
consent: ToolConsentSchema.optional(),
|
|
@@ -9204,7 +9328,7 @@ var init_b64u = __esm(() => {
|
|
|
9204
9328
|
});
|
|
9205
9329
|
|
|
9206
9330
|
// packages/core/dist/version.js
|
|
9207
|
-
var VERSION = "0.
|
|
9331
|
+
var VERSION = "0.9.0";
|
|
9208
9332
|
|
|
9209
9333
|
// packages/core/dist/jcs.js
|
|
9210
9334
|
function hasLoneSurrogate(text) {
|
|
@@ -11061,8 +11185,11 @@ var init_jelly_host = __esm(() => {
|
|
|
11061
11185
|
"people.search",
|
|
11062
11186
|
"people.get",
|
|
11063
11187
|
"grant.list",
|
|
11188
|
+
"reach.get",
|
|
11064
11189
|
"request.list",
|
|
11065
11190
|
"sessions.list",
|
|
11191
|
+
"tasks.list",
|
|
11192
|
+
"tasks.get",
|
|
11066
11193
|
"notice.subscribe",
|
|
11067
11194
|
"notice.list",
|
|
11068
11195
|
"device.list"
|
|
@@ -12387,7 +12514,17 @@ function verifyConsentGrant(raw, ctx) {
|
|
|
12387
12514
|
}
|
|
12388
12515
|
}
|
|
12389
12516
|
function grantSubjectKey(subject) {
|
|
12390
|
-
return "person" in subject ? `person:${subject.person}` : `conv:${subject.conv}`;
|
|
12517
|
+
return "person" in subject ? `person:${subject.person}` : ("conv" in subject) ? `conv:${subject.conv}` : `relation:${subject.relation}`;
|
|
12518
|
+
}
|
|
12519
|
+
function reachChoices(outside, org) {
|
|
12520
|
+
const message = outside === undefined ? undefined : outside.chat === "open" ? "open" : outside.chat === "blocked" ? "never" : "ask";
|
|
12521
|
+
const ask = org?.ask === "open" ? "open" : org?.ask === "request" ? "ask" : org?.ask === "no" ? "never" : undefined;
|
|
12522
|
+
return { ...message ? { message } : {}, ...ask ? { ask } : {} };
|
|
12523
|
+
}
|
|
12524
|
+
function reachGrant(group, value) {
|
|
12525
|
+
if (group === "message")
|
|
12526
|
+
return { subject: { relation: "outside" }, levels: value === "open" ? { chat: "open" } : value === "never" ? { chat: "blocked" } : {} };
|
|
12527
|
+
return { subject: { relation: "org" }, levels: { ask: value === "open" ? "open" : value === "never" ? "no" : "request" } };
|
|
12391
12528
|
}
|
|
12392
12529
|
function preferGrant(a, b, current) {
|
|
12393
12530
|
if (a.ts !== b.ts)
|
|
@@ -12429,8 +12566,18 @@ function resignGrant(identity, grant, opts) {
|
|
|
12429
12566
|
function levelsOf(grant) {
|
|
12430
12567
|
return defined({ chat: grant.chat, agentMail: grant.agentMail, ask: grant.ask, trustedUntil: grant.trustedUntil, discoverable: grant.discoverable });
|
|
12431
12568
|
}
|
|
12432
|
-
function
|
|
12569
|
+
function baseLevels(relation, reach) {
|
|
12433
12570
|
const base = DEFAULTS[relation];
|
|
12571
|
+
if (relation === "org")
|
|
12572
|
+
return reach.ask === undefined ? base : { ...base, ask: reach.ask === "open" ? "open" : reach.ask === "never" ? "no" : "request" };
|
|
12573
|
+
if (reach.message === "never")
|
|
12574
|
+
return { ...base, chat: "blocked" };
|
|
12575
|
+
if (relation === "stranger" && reach.message === "open")
|
|
12576
|
+
return { ...base, chat: "open" };
|
|
12577
|
+
return base;
|
|
12578
|
+
}
|
|
12579
|
+
function effectivePolicy(relation, person, channels, now, reach = {}) {
|
|
12580
|
+
const base = baseLevels(relation, reach);
|
|
12434
12581
|
const channelAsk = channels.map((grant) => grant.ask).filter((level) => level !== undefined);
|
|
12435
12582
|
const channelMail = channels.map((grant) => grant.agentMail).filter((level) => level !== undefined);
|
|
12436
12583
|
const ask = person?.ask ?? (channelAsk.length > 0 ? channelAsk.reduce((a, b) => ASK_RANK[b] > ASK_RANK[a] ? b : a) : undefined);
|
|
@@ -12448,15 +12595,19 @@ function decide(args) {
|
|
|
12448
12595
|
const { inbound } = args;
|
|
12449
12596
|
if (args.relation === "self" || args.sender === args.self)
|
|
12450
12597
|
return { action: "deliver", consent: "self" };
|
|
12451
|
-
const policy = effectivePolicy(args.relation, args.grant, args.channels, args.now);
|
|
12598
|
+
const policy = effectivePolicy(args.relation, args.grant, args.channels, args.now, args.reach);
|
|
12452
12599
|
const trusted = (chain) => policy.trustedUntil !== undefined && chain.every((person) => person === args.sender || person === args.self);
|
|
12453
12600
|
if (inbound.kind === "answer")
|
|
12454
12601
|
return { action: "deliver", consent: trusted([args.sender]) ? "trusted" : "guarded" };
|
|
12455
|
-
|
|
12602
|
+
const blocked = policy.chat === "blocked";
|
|
12603
|
+
if (blocked && policy.granted.chat)
|
|
12456
12604
|
return { action: "refuse", code: "refused" };
|
|
12457
12605
|
const stranger = args.relation === "stranger";
|
|
12606
|
+
const defaultCode = (outside) => outside && !blocked ? "request_required" : "refused";
|
|
12458
12607
|
switch (inbound.kind) {
|
|
12459
12608
|
case "op":
|
|
12609
|
+
if (blocked)
|
|
12610
|
+
return { action: "refuse", code: "refused" };
|
|
12460
12611
|
if (stranger)
|
|
12461
12612
|
return { action: "refuse", code: "request_required" };
|
|
12462
12613
|
return { action: "deliver", consent: "guarded" };
|
|
@@ -12464,13 +12615,17 @@ function decide(args) {
|
|
|
12464
12615
|
if (inbound.agent) {
|
|
12465
12616
|
if (policy.agentMail === "open")
|
|
12466
12617
|
return { action: "deliver", consent: "guarded" };
|
|
12467
|
-
return { action: "refuse", code: stranger && !policy.granted.agentMail
|
|
12618
|
+
return { action: "refuse", code: defaultCode(stranger && !policy.granted.agentMail) };
|
|
12468
12619
|
}
|
|
12620
|
+
if (blocked)
|
|
12621
|
+
return { action: "refuse", code: "refused" };
|
|
12469
12622
|
if (policy.chat === "request_required")
|
|
12470
12623
|
return { action: "refuse", code: "request_required" };
|
|
12471
12624
|
return policy.chat === "muted" ? { action: "deliver", consent: "guarded", silent: true } : { action: "deliver", consent: "guarded" };
|
|
12472
12625
|
case "request":
|
|
12473
|
-
if (
|
|
12626
|
+
if (blocked)
|
|
12627
|
+
return { action: "refuse", code: "refused" };
|
|
12628
|
+
if (policy.chat === "request_required")
|
|
12474
12629
|
return { action: "hold" };
|
|
12475
12630
|
return policy.chat === "muted" ? { action: "deliver", consent: "guarded", silent: true } : { action: "deliver", consent: "guarded" };
|
|
12476
12631
|
case "ask":
|
|
@@ -12478,7 +12633,7 @@ function decide(args) {
|
|
|
12478
12633
|
return { action: "deliver", consent: trusted(inbound.chain) ? "trusted" : "guarded" };
|
|
12479
12634
|
if (policy.ask === "request")
|
|
12480
12635
|
return { action: "hold" };
|
|
12481
|
-
return { action: "refuse", code: !policy.granted.ask && (stranger || args.relation === "contact")
|
|
12636
|
+
return { action: "refuse", code: defaultCode(!policy.granted.ask && (stranger || args.relation === "contact")) };
|
|
12482
12637
|
}
|
|
12483
12638
|
}
|
|
12484
12639
|
function defined(value) {
|
|
@@ -12543,8 +12698,8 @@ function verifyContactCard(raw, ctx) {
|
|
|
12543
12698
|
throw asMessagingError(error, "card");
|
|
12544
12699
|
}
|
|
12545
12700
|
}
|
|
12546
|
-
function cardCan(relation, grant, now) {
|
|
12547
|
-
const policy = effectivePolicy(relation, grant, [], now);
|
|
12701
|
+
function cardCan(relation, grant, now, reach = {}) {
|
|
12702
|
+
const policy = effectivePolicy(relation, grant, [], now, reach);
|
|
12548
12703
|
return {
|
|
12549
12704
|
chat: policy.chat === "open" || policy.chat === "muted",
|
|
12550
12705
|
agentMail: policy.agentMail === "open",
|
|
@@ -12718,6 +12873,9 @@ function canTransition(from, to) {
|
|
|
12718
12873
|
function isTerminal(state) {
|
|
12719
12874
|
return TERMINAL_STATES.includes(state);
|
|
12720
12875
|
}
|
|
12876
|
+
function isInterrupted(state) {
|
|
12877
|
+
return INTERRUPTED_STATES.includes(state);
|
|
12878
|
+
}
|
|
12721
12879
|
var NEXT;
|
|
12722
12880
|
var init_task = __esm(() => {
|
|
12723
12881
|
init_a2a();
|
|
@@ -13960,6 +14118,7 @@ var init_present = __esm(() => {
|
|
|
13960
14118
|
init_errors3();
|
|
13961
14119
|
init_peer_text();
|
|
13962
14120
|
init_common();
|
|
14121
|
+
init_ipc();
|
|
13963
14122
|
init_text();
|
|
13964
14123
|
PART_CHARS = LIMITS.toolTextChars;
|
|
13965
14124
|
OWN = createFence({ tag: PEER_FENCE_TAG });
|
|
@@ -18665,6 +18824,12 @@ class Store {
|
|
|
18665
18824
|
const rows = person === undefined ? this.#all("SELECT * FROM contact_requests ORDER BY at, id") : this.#all("SELECT * FROM contact_requests WHERE person = ? ORDER BY at, id", person);
|
|
18666
18825
|
return rows.map(contactRequestRow);
|
|
18667
18826
|
}
|
|
18827
|
+
legacyOutgoingRequests(now) {
|
|
18828
|
+
return this.#all(`SELECT t.id AS id, substr(t.room, 4) AS person, t.at AS at FROM transcript t
|
|
18829
|
+
WHERE t.direction = 'out' AND t.room LIKE 'dm:%'
|
|
18830
|
+
AND EXISTS (SELECT 1 FROM chat_sent c WHERE c.line = t.id AND c.stage = 'sent' AND c.held_at IS NOT NULL AND c.exp > ?)
|
|
18831
|
+
ORDER BY t.at, t.id`, now).map((r) => ({ id: String(r.id), person: String(r.person), at: Number(r.at) }));
|
|
18832
|
+
}
|
|
18668
18833
|
contactRequestCount() {
|
|
18669
18834
|
return Number(this.#get("SELECT COUNT(*) AS n FROM contact_requests")?.n ?? 0);
|
|
18670
18835
|
}
|
|
@@ -18694,38 +18859,56 @@ class Store {
|
|
|
18694
18859
|
return this.#run("DELETE FROM contact_suggestions WHERE at < ?", before);
|
|
18695
18860
|
}
|
|
18696
18861
|
putGrant(subject, grant) {
|
|
18862
|
+
const relation = relationOf(subject);
|
|
18863
|
+
if (relation !== undefined) {
|
|
18864
|
+
this.#run("INSERT INTO reach_grants (relation, grant) VALUES (?, ?) ON CONFLICT (relation) DO UPDATE SET grant = excluded.grant", relation, JSON.stringify(grant));
|
|
18865
|
+
return;
|
|
18866
|
+
}
|
|
18697
18867
|
this.#run("INSERT INTO grants (subject, grant) VALUES (?, ?) ON CONFLICT (subject) DO UPDATE SET grant = excluded.grant", subject, JSON.stringify(grant));
|
|
18698
18868
|
}
|
|
18699
18869
|
grant(subject) {
|
|
18700
|
-
const
|
|
18870
|
+
const relation = relationOf(subject);
|
|
18871
|
+
const row = relation !== undefined ? this.#get("SELECT grant FROM reach_grants WHERE relation = ?", relation) : this.#get("SELECT grant FROM grants WHERE subject = ?", subject);
|
|
18701
18872
|
return row === undefined ? undefined : JSON.parse(String(row.grant));
|
|
18702
18873
|
}
|
|
18703
18874
|
grants() {
|
|
18704
|
-
return
|
|
18875
|
+
return [
|
|
18876
|
+
...this.#all("SELECT subject, grant FROM grants ORDER BY subject").map((row) => ({ subject: String(row.subject), grant: JSON.parse(String(row.grant)) })),
|
|
18877
|
+
...this.#all("SELECT relation, grant FROM reach_grants ORDER BY relation").map((row) => ({ subject: `${RELATION}${String(row.relation)}`, grant: JSON.parse(String(row.grant)) }))
|
|
18878
|
+
];
|
|
18705
18879
|
}
|
|
18706
18880
|
grantCount() {
|
|
18707
18881
|
return Number(this.#get("SELECT COUNT(*) AS n FROM grants")?.n ?? 0);
|
|
18708
18882
|
}
|
|
18709
18883
|
deleteGrant(subject) {
|
|
18710
|
-
|
|
18884
|
+
const relation = relationOf(subject);
|
|
18885
|
+
if (relation !== undefined)
|
|
18886
|
+
this.#run("DELETE FROM reach_grants WHERE relation = ?", relation);
|
|
18887
|
+
else
|
|
18888
|
+
this.#run("DELETE FROM grants WHERE subject = ?", subject);
|
|
18711
18889
|
}
|
|
18712
18890
|
putGrantReview(subject, id, levels, at) {
|
|
18713
|
-
|
|
18891
|
+
const relation = relationOf(subject);
|
|
18892
|
+
this.#run(relation !== undefined ? "INSERT INTO reach_reviews (relation, id, levels, at) VALUES (?, ?, ?, ?) ON CONFLICT (relation) DO UPDATE SET id = excluded.id, levels = excluded.levels, at = excluded.at" : "INSERT INTO grant_reviews (subject, id, levels, at) VALUES (?, ?, ?, ?) ON CONFLICT (subject) DO UPDATE SET id = excluded.id, levels = excluded.levels, at = excluded.at", relation ?? subject, id, JSON.stringify(levels), at);
|
|
18714
18893
|
}
|
|
18715
18894
|
grantReviews() {
|
|
18716
|
-
|
|
18717
|
-
|
|
18718
|
-
|
|
18719
|
-
|
|
18720
|
-
|
|
18721
|
-
}));
|
|
18895
|
+
const row = (subject, r) => ({ subject, id: String(r.id), levels: JSON.parse(String(r.levels)), at: Number(r.at) });
|
|
18896
|
+
return [
|
|
18897
|
+
...this.#all("SELECT * FROM grant_reviews ORDER BY at DESC, subject").map((r) => row(String(r.subject), r)),
|
|
18898
|
+
...this.#all("SELECT * FROM reach_reviews ORDER BY at DESC, relation").map((r) => row(`${RELATION}${String(r.relation)}`, r))
|
|
18899
|
+
];
|
|
18722
18900
|
}
|
|
18723
18901
|
grantReview(subject) {
|
|
18724
|
-
const
|
|
18902
|
+
const relation = relationOf(subject);
|
|
18903
|
+
const row = relation !== undefined ? this.#get("SELECT id FROM reach_reviews WHERE relation = ?", relation) : this.#get("SELECT id FROM grant_reviews WHERE subject = ?", subject);
|
|
18725
18904
|
return row === undefined ? undefined : { id: String(row.id) };
|
|
18726
18905
|
}
|
|
18727
18906
|
deleteGrantReview(subject) {
|
|
18728
|
-
|
|
18907
|
+
const relation = relationOf(subject);
|
|
18908
|
+
if (relation !== undefined)
|
|
18909
|
+
this.#run("DELETE FROM reach_reviews WHERE relation = ?", relation);
|
|
18910
|
+
else
|
|
18911
|
+
this.#run("DELETE FROM grant_reviews WHERE subject = ?", subject);
|
|
18729
18912
|
}
|
|
18730
18913
|
sentUndelivered(now) {
|
|
18731
18914
|
return this.#all("SELECT * FROM sent WHERE exp <= ? AND stage IN ('accepted', 'relayed')", now).map(sentRow);
|
|
@@ -18795,12 +18978,14 @@ class Store {
|
|
|
18795
18978
|
}
|
|
18796
18979
|
}
|
|
18797
18980
|
listTasks(filter) {
|
|
18798
|
-
const where = ["
|
|
18799
|
-
const params = [
|
|
18981
|
+
const where = ["1 = 1"];
|
|
18982
|
+
const params = [];
|
|
18800
18983
|
const add = (clause, ...values) => {
|
|
18801
18984
|
where.push(clause);
|
|
18802
18985
|
params.push(...values);
|
|
18803
18986
|
};
|
|
18987
|
+
if (filter.key !== undefined)
|
|
18988
|
+
add("key = ?", filter.key);
|
|
18804
18989
|
if (filter.role !== undefined)
|
|
18805
18990
|
add("role = ?", filter.role);
|
|
18806
18991
|
if (filter.state !== undefined)
|
|
@@ -19187,7 +19372,7 @@ CREATE TABLE IF NOT EXISTS contact_requests (
|
|
|
19187
19372
|
) STRICT;
|
|
19188
19373
|
CREATE INDEX IF NOT EXISTS contact_requests_person ON contact_requests (person);
|
|
19189
19374
|
CREATE TABLE IF NOT EXISTS contact_suggestions (id TEXT PRIMARY KEY, session TEXT NOT NULL, email TEXT NOT NULL, at INTEGER NOT NULL) STRICT;
|
|
19190
|
-
`, MIGRATIONS, STORE_SCHEMA, kvGet = (db, k) => {
|
|
19375
|
+
`, MIGRATIONS, RELATION = "relation:", relationOf = (subject) => subject.startsWith(RELATION) ? subject.slice(RELATION.length) : undefined, STORE_SCHEMA, kvGet = (db, k) => {
|
|
19191
19376
|
const row = db.prepare("SELECT v FROM kv WHERE k = ?").get(k);
|
|
19192
19377
|
return row?.v === undefined || row.v === null ? undefined : String(row.v);
|
|
19193
19378
|
}, kvSet = (db, k, v) => {
|
|
@@ -19263,6 +19448,10 @@ CREATE TABLE IF NOT EXISTS conv_evidence (
|
|
|
19263
19448
|
at INTEGER NOT NULL,
|
|
19264
19449
|
UNIQUE (cid, digest, code)
|
|
19265
19450
|
) STRICT;
|
|
19451
|
+
`,
|
|
19452
|
+
`
|
|
19453
|
+
CREATE TABLE IF NOT EXISTS reach_grants (relation TEXT PRIMARY KEY, grant TEXT NOT NULL) STRICT;
|
|
19454
|
+
CREATE TABLE IF NOT EXISTS reach_reviews (relation TEXT PRIMARY KEY, id TEXT NOT NULL, levels TEXT NOT NULL, at INTEGER NOT NULL) STRICT;
|
|
19266
19455
|
`
|
|
19267
19456
|
];
|
|
19268
19457
|
STORE_SCHEMA = 1 + MIGRATIONS.length;
|
|
@@ -19854,7 +20043,7 @@ class ConversationEngine {
|
|
|
19854
20043
|
const pending = this.options.store.convPending(cid);
|
|
19855
20044
|
if (rekey.cause === "device" && pending) {
|
|
19856
20045
|
const first = admitConvEvent(parseWire(pending.event));
|
|
19857
|
-
if (first.class === "op" && first.body.op === "rekey" && first.body.cause === "device" && first.body.device === rekey.device)
|
|
20046
|
+
if (first.class === "op" && first.body.op === "rekey" && first.body.cause === "device" && first.body.device === rekey.device && (rekey.until === undefined || first.body.until !== undefined && first.body.until <= rekey.until))
|
|
19858
20047
|
return first.id;
|
|
19859
20048
|
return await this.#queueRekey(cid, rekey, true);
|
|
19860
20049
|
}
|
|
@@ -20637,6 +20826,14 @@ class ConversationIpc {
|
|
|
20637
20826
|
inboxNotify(key, cid) {
|
|
20638
20827
|
this.#label(this.#current(), cid, { key, own: this.options.own().person });
|
|
20639
20828
|
}
|
|
20829
|
+
taskChanged(side) {
|
|
20830
|
+
this.#ring.push({ pos: ++this.#pos, task: { id: side.id, role: side.role } });
|
|
20831
|
+
if (this.#ring.length > REPLAY)
|
|
20832
|
+
this.#floor = this.#ring.shift().pos;
|
|
20833
|
+
this.options.put("conv.ipc.pos", String(this.#pos));
|
|
20834
|
+
for (const wake of [...this.#waiters])
|
|
20835
|
+
wake();
|
|
20836
|
+
}
|
|
20640
20837
|
bind(engine) {
|
|
20641
20838
|
if (this.#engine === engine)
|
|
20642
20839
|
return;
|
|
@@ -20732,7 +20929,7 @@ class ConversationIpc {
|
|
|
20732
20929
|
if (authority?.scope === "host")
|
|
20733
20930
|
throw new MessagingError("forbidden", "conversation content needs an action grant");
|
|
20734
20931
|
const name = method;
|
|
20735
|
-
const result = await this.call(name, params, ctx);
|
|
20932
|
+
const result = await this.call(name, params, ctx, authority);
|
|
20736
20933
|
const parsed = LIVE_CONVERSATION_METHODS[name].result.parse(result);
|
|
20737
20934
|
if (Buffer.byteLength(JSON.stringify(parsed)) > RESPONSE_BYTES)
|
|
20738
20935
|
throw new MessagingError("too_large", "conversation response exceeds the byte budget");
|
|
@@ -20833,7 +21030,13 @@ class ConversationIpc {
|
|
|
20833
21030
|
throw new MessagingError("forbidden", "the session cannot read this conversation");
|
|
20834
21031
|
if (access.access === "ask")
|
|
20835
21032
|
return HOLD;
|
|
20836
|
-
const
|
|
21033
|
+
const orgs = new Map;
|
|
21034
|
+
const name = async (p) => {
|
|
21035
|
+
const profile = await this.#name(p);
|
|
21036
|
+
orgs.set(p, profile.org);
|
|
21037
|
+
return profile;
|
|
21038
|
+
};
|
|
21039
|
+
const all = await projectEvents(engine, cid, scope.participant, name, (p) => !this.options.blocked(p, orgs.get(p) ?? "none"));
|
|
20837
21040
|
let visible = all.filter((e) => e.seq >= access.since && e.at >= access.after);
|
|
20838
21041
|
if (scope.agent && this.#state(engine, cid, scope).state.agentContext === "none") {
|
|
20839
21042
|
const roots = new Set(visible.filter((e) => e.author.p === scope.participant || e.mentions?.some((m) => ("p" in m) && m.p === scope.participant)).map((e) => e.thread ?? e.id));
|
|
@@ -20908,10 +21111,30 @@ class ConversationIpc {
|
|
|
20908
21111
|
return { events: [{ type: "gap", pos: this.#pos }], pos: this.#pos };
|
|
20909
21112
|
const events = [];
|
|
20910
21113
|
let pos = after;
|
|
20911
|
-
|
|
21114
|
+
const range = this.#ring.filter((i) => i.pos > after);
|
|
21115
|
+
for (const [index, item] of range.entries()) {
|
|
20912
21116
|
pos = item.pos;
|
|
20913
|
-
const c = item.change;
|
|
20914
21117
|
let rendered;
|
|
21118
|
+
if ("task" in item) {
|
|
21119
|
+
const side = item.task;
|
|
21120
|
+
if (scope.key !== undefined || scope.ctx.tier !== "admin" || !this.options.task)
|
|
21121
|
+
continue;
|
|
21122
|
+
if (range.slice(index + 1).some((later) => ("task" in later) && later.task.id === side.id && later.task.role === side.role))
|
|
21123
|
+
continue;
|
|
21124
|
+
const task = await this.options.task(side.id, side.role, scope.ctx, scope.authority);
|
|
21125
|
+
if (!task)
|
|
21126
|
+
continue;
|
|
21127
|
+
rendered = { type: "task", pos, task };
|
|
21128
|
+
if (Buffer.byteLength(JSON.stringify([...events, rendered])) > RESPONSE_BYTES - 4096) {
|
|
21129
|
+
pos = item.pos - 1;
|
|
21130
|
+
break;
|
|
21131
|
+
}
|
|
21132
|
+
events.push(rendered);
|
|
21133
|
+
if (events.length >= (p.limit ?? 50))
|
|
21134
|
+
break;
|
|
21135
|
+
continue;
|
|
21136
|
+
}
|
|
21137
|
+
const c = item.change;
|
|
20915
21138
|
try {
|
|
20916
21139
|
if (this.#access(engine, c.cid, scope).access !== "allow")
|
|
20917
21140
|
continue;
|
|
@@ -20981,7 +21204,7 @@ class ConversationIpc {
|
|
|
20981
21204
|
result = await read();
|
|
20982
21205
|
return result;
|
|
20983
21206
|
}
|
|
20984
|
-
async call(method, raw, ctx) {
|
|
21207
|
+
async call(method, raw, ctx, authority) {
|
|
20985
21208
|
if (ctx.tier === "agent" && CONVERSATION_TIERS[method] !== "agent")
|
|
20986
21209
|
throw new MessagingError("forbidden", "this method is human-only");
|
|
20987
21210
|
const p = LIVE_CONVERSATION_METHODS[method].params.parse(raw);
|
|
@@ -21003,7 +21226,7 @@ class ConversationIpc {
|
|
|
21003
21226
|
const sessions = this.options.sessions().map((s) => ({ ...s, seats: engine?.list().filter((c) => c.state?.participants.some((p) => p.p === `${this.options.own().person}/${s.agent}` && p.state === "member")).map((c) => c.cid) ?? [] }));
|
|
21004
21227
|
return { sessions };
|
|
21005
21228
|
}
|
|
21006
|
-
const scope = this.#scope("key" in p ? p.key : undefined, ctx);
|
|
21229
|
+
const scope = { ...this.#scope("key" in p ? p.key : undefined, ctx), authority };
|
|
21007
21230
|
const engine = this.#current();
|
|
21008
21231
|
const { cursor: _cursor, ...position } = p;
|
|
21009
21232
|
const query = canonicalize({ method, person: scope.own, session: scope.key ?? null, ...position });
|
|
@@ -21212,8 +21435,11 @@ class ConversationIpc {
|
|
|
21212
21435
|
await this.options.current?.(p.participants.map((s) => participantPerson(s.p)));
|
|
21213
21436
|
}
|
|
21214
21437
|
const { cid, ...fields } = p;
|
|
21215
|
-
|
|
21438
|
+
for (let drains = 0;method !== "conv.meta" && engine.pending(cid).length > 0; drains++) {
|
|
21439
|
+
if (drains === CONTROL_DRAINS)
|
|
21440
|
+
throw new MessagingError("conv_conflict", "the conversation is busy sending; try again");
|
|
21216
21441
|
await engine.flush(cid);
|
|
21442
|
+
}
|
|
21217
21443
|
if (method === "conv.meta")
|
|
21218
21444
|
await engine.queueMeta(cid, p.meta);
|
|
21219
21445
|
else
|
|
@@ -21241,7 +21467,7 @@ class ConversationIpc {
|
|
|
21241
21467
|
}
|
|
21242
21468
|
}
|
|
21243
21469
|
}
|
|
21244
|
-
var DEFERRED_LINES = 50, DEFERRED_MS, HOLD, REPLAY = 1e4, RESPONSE_BYTES;
|
|
21470
|
+
var DEFERRED_LINES = 50, DEFERRED_MS, CONTROL_DRAINS = 3, HOLD, REPLAY = 1e4, RESPONSE_BYTES;
|
|
21245
21471
|
var init_conv_ipc = __esm(() => {
|
|
21246
21472
|
init_dist();
|
|
21247
21473
|
init_conv_project();
|
|
@@ -22140,19 +22366,19 @@ function expectation(view, target, wake, policy) {
|
|
|
22140
22366
|
const s = view.sessions.find((row) => row.name === target.session);
|
|
22141
22367
|
if (!s)
|
|
22142
22368
|
return;
|
|
22143
|
-
return
|
|
22369
|
+
return reach2({ kept: s.unregisteredAt !== undefined, tier: s.tier, state: s.state, wakes: policy.wake(s) }, wake);
|
|
22144
22370
|
}
|
|
22145
22371
|
const s = view.peers.find((row) => row.machine === target.machine)?.presence.sessions.find((info) => info.session === target.session);
|
|
22146
22372
|
if (!s)
|
|
22147
22373
|
return;
|
|
22148
22374
|
if (!view.online.has(target.machine))
|
|
22149
22375
|
return "offline";
|
|
22150
|
-
return
|
|
22376
|
+
return reach2({ kept: s.kept === true, tier: s.tier, state: s.state, wakes: s.wake === true }, wake);
|
|
22151
22377
|
}
|
|
22152
22378
|
function withExpect(expect) {
|
|
22153
22379
|
return expect === undefined ? {} : { expect };
|
|
22154
22380
|
}
|
|
22155
|
-
function
|
|
22381
|
+
function reach2(s, wake) {
|
|
22156
22382
|
if (s.kept)
|
|
22157
22383
|
return wake && s.wakes ? "wake" : "kept";
|
|
22158
22384
|
if (s.tier === "pull")
|
|
@@ -22196,7 +22422,7 @@ function fitPresence(sessions, baseBytes, limit) {
|
|
|
22196
22422
|
var FEATURES;
|
|
22197
22423
|
var init_directory2 = __esm(() => {
|
|
22198
22424
|
init_dist();
|
|
22199
|
-
FEATURES = ["tasks", "conversations"];
|
|
22425
|
+
FEATURES = ["tasks", "conversations", "reach"];
|
|
22200
22426
|
});
|
|
22201
22427
|
|
|
22202
22428
|
// packages/daemon/dist/delegation.js
|
|
@@ -22905,6 +23131,22 @@ class DirectoryIdentity {
|
|
|
22905
23131
|
const head = this.#head;
|
|
22906
23132
|
return head !== undefined && this.#options.now() - head.sth.ts > maxAgeMs + LIMITS.epochHeartbeatMs;
|
|
22907
23133
|
}
|
|
23134
|
+
async freshen(persons, batch = LIMITS.dirLookupPersons) {
|
|
23135
|
+
const size1 = Math.max(1, Math.min(batch, LIMITS.dirLookupPersons));
|
|
23136
|
+
const now = this.#options.now();
|
|
23137
|
+
const size = this.#head?.sth.size ?? 0;
|
|
23138
|
+
const stale = [...new Set(persons)].filter((person) => {
|
|
23139
|
+
if (!isHosted(person))
|
|
23140
|
+
return false;
|
|
23141
|
+
const held = this.#persons.get(person);
|
|
23142
|
+
return !held || now - held.fetchedAt >= LIMITS.dirFreshMs || held.size < size;
|
|
23143
|
+
});
|
|
23144
|
+
if (stale.length === 0 && !this.#headOlderThan(LIMITS.dirFreshMs))
|
|
23145
|
+
return;
|
|
23146
|
+
await this.#latest();
|
|
23147
|
+
for (let i = 0;i < stale.length; i += size1)
|
|
23148
|
+
await this.#fetch(stale.slice(i, i + size1));
|
|
23149
|
+
}
|
|
22908
23150
|
async profile(person) {
|
|
22909
23151
|
if (!isHosted(person))
|
|
22910
23152
|
throw new MessagingError("not_found", "no such person in this realm");
|
|
@@ -24041,8 +24283,10 @@ class JellyAuth {
|
|
|
24041
24283
|
this.#deps = deps;
|
|
24042
24284
|
}
|
|
24043
24285
|
authorize(call) {
|
|
24044
|
-
const
|
|
24045
|
-
|
|
24286
|
+
const params = call.params !== null && typeof call.params === "object" ? call.params : undefined;
|
|
24287
|
+
const sessionCall = params !== undefined && typeof params.key === "string";
|
|
24288
|
+
const tokenCall = params !== undefined && Object.hasOwn(params, "token");
|
|
24289
|
+
if (call.ctx.tier === "agent" || AGENT.has(call.method) && !tokenCall && (!Object.hasOwn(CONVERSATION_METHODS, call.method) || sessionCall))
|
|
24046
24290
|
return;
|
|
24047
24291
|
const refuse = (error) => {
|
|
24048
24292
|
this.#deps.log("warn", "jelly.grant.refused", { method: call.method, code: error.code, reason: error.message });
|
|
@@ -24075,7 +24319,7 @@ class JellyAuth {
|
|
|
24075
24319
|
...grant.dev !== undefined ? { dev: grant.dev.slice(0, 200) } : {},
|
|
24076
24320
|
...grant.mbr !== undefined ? { mbr: grant.mbr.slice(0, 200) } : {}
|
|
24077
24321
|
});
|
|
24078
|
-
return { scope: grant.scope, expiresAt: (grant.exp + JELLY_HOST_SKEW_S) * 1000 };
|
|
24322
|
+
return { scope: grant.scope, expiresAt: (grant.exp + JELLY_HOST_SKEW_S) * 1000, ...grant.dev !== undefined ? { dev: grant.dev } : {}, ...grant.mbr !== undefined ? { mbr: grant.mbr } : {} };
|
|
24079
24323
|
}
|
|
24080
24324
|
ticket(p) {
|
|
24081
24325
|
const refuse = (error) => {
|
|
@@ -24409,6 +24653,9 @@ class Notices {
|
|
|
24409
24653
|
held(row) {
|
|
24410
24654
|
this.#file({ id: `r_${row.id}`, category: "request", person: row.person, source: row.id, at: row.at });
|
|
24411
24655
|
}
|
|
24656
|
+
task(row) {
|
|
24657
|
+
this.#file({ id: `m_task${row.taskId}`, category: "message", person: row.person, source: `${TASK_SOURCE}${row.taskId}`, at: row.at });
|
|
24658
|
+
}
|
|
24412
24659
|
alert(kind, at) {
|
|
24413
24660
|
this.#file({ id: `a_${kind.replace(/_/g, "")}${at}`, category: "alert", source: kind, at });
|
|
24414
24661
|
}
|
|
@@ -24421,7 +24668,7 @@ class Notices {
|
|
|
24421
24668
|
#file(row) {
|
|
24422
24669
|
if (!this.#host.store.putNotice(row))
|
|
24423
24670
|
return;
|
|
24424
|
-
this.#host.emit({ type: "notice", notice: { id: row.id, category: row.category, at: row.at } });
|
|
24671
|
+
this.#host.emit({ type: "notice", notice: { id: row.id, category: row.category, ...row.source.startsWith(TASK_SOURCE) ? { key: `task:${row.source.slice(TASK_SOURCE.length)}` } : {}, at: row.at } });
|
|
24425
24672
|
}
|
|
24426
24673
|
async subscribe(ctx, p, until) {
|
|
24427
24674
|
const rows = this.#host.store.noticesAfter(p.after, REPLAY2);
|
|
@@ -24477,6 +24724,8 @@ class Notices {
|
|
|
24477
24724
|
const own = row.person === this.#host.me();
|
|
24478
24725
|
notice.from = profile ? { name: profile.name.slice(0, 200), ...profile.email !== undefined ? { email: profile.email.slice(0, 320) } : {}, org: profile.org } : { name: row.person, org: own ? "same" : "none" };
|
|
24479
24726
|
}
|
|
24727
|
+
if (row.category === "message" && row.source.startsWith(TASK_SOURCE))
|
|
24728
|
+
notice.key = `task:${row.source.slice(TASK_SOURCE.length)}`;
|
|
24480
24729
|
if (row.category === "alert" && row.source.startsWith("device:")) {
|
|
24481
24730
|
const device = this.#host.device?.(Number(row.source.slice("device:".length)));
|
|
24482
24731
|
if (device)
|
|
@@ -24502,7 +24751,7 @@ function deviceLine(d) {
|
|
|
24502
24751
|
const line = d.change === "added" ? `New device on your account: ${what}, ${vouchWords(d)}.` : d.change === "vouched" ? `${what} on your account is now vouched by Jelly ID: ${vouchWords(d)}.` : d.change === "replaced" ? `'${d.replaces?.label ?? "a device"}' was replaced by ${what}, ${vouchWords(d)}.` : `${what} was removed from your account.`;
|
|
24503
24752
|
return line.length <= NOTICE_LINE_CHARS ? line : `${line.slice(0, NOTICE_LINE_CHARS - 1)}\u2026`;
|
|
24504
24753
|
}
|
|
24505
|
-
var ALERT_TEXT, REPLAY2 = 500;
|
|
24754
|
+
var ALERT_TEXT, TASK_SOURCE = "task:", REPLAY2 = 500;
|
|
24506
24755
|
var init_notices = __esm(() => {
|
|
24507
24756
|
init_dist();
|
|
24508
24757
|
ALERT_TEXT = {
|
|
@@ -24512,6 +24761,68 @@ var init_notices = __esm(() => {
|
|
|
24512
24761
|
};
|
|
24513
24762
|
});
|
|
24514
24763
|
|
|
24764
|
+
// packages/daemon/dist/task-view.js
|
|
24765
|
+
function plainCapped(text, max, singleLine) {
|
|
24766
|
+
let budget = max;
|
|
24767
|
+
for (;; ) {
|
|
24768
|
+
const out = cleanPeerText(text, { singleLine, maxChars: budget }).trim();
|
|
24769
|
+
if (out.length <= max)
|
|
24770
|
+
return out.length > 0 ? out : undefined;
|
|
24771
|
+
budget = Math.max(0, budget - (out.length - max));
|
|
24772
|
+
}
|
|
24773
|
+
}
|
|
24774
|
+
function executorWords(record) {
|
|
24775
|
+
const message = record.task.status.message;
|
|
24776
|
+
return message !== undefined && (record.role === "executor" || message.metadata?.[URI.provenance] !== undefined);
|
|
24777
|
+
}
|
|
24778
|
+
function signInTool(record) {
|
|
24779
|
+
if (!executorWords(record))
|
|
24780
|
+
return;
|
|
24781
|
+
const value = record.task.status.message?.metadata?.[URI.signIn];
|
|
24782
|
+
if (typeof value !== "object" || value === null || Array.isArray(value))
|
|
24783
|
+
return;
|
|
24784
|
+
const tool = value.tool;
|
|
24785
|
+
return typeof tool === "string" ? plainCapped(tool, TASK_SIGN_IN_TOOL_CHARS, true) : undefined;
|
|
24786
|
+
}
|
|
24787
|
+
function refusalReason2(record) {
|
|
24788
|
+
if (!executorWords(record))
|
|
24789
|
+
return;
|
|
24790
|
+
const text = (record.task.status.message?.parts ?? []).flatMap((part) => typeof part.text === "string" ? [part.text] : []).join(`
|
|
24791
|
+
`);
|
|
24792
|
+
return plainCapped(text, TASK_REFUSAL_CHARS, false);
|
|
24793
|
+
}
|
|
24794
|
+
function withTaskView(record, me, named) {
|
|
24795
|
+
const view = { ...record };
|
|
24796
|
+
delete view.agent;
|
|
24797
|
+
delete view.person;
|
|
24798
|
+
delete view.org;
|
|
24799
|
+
delete view.signIn;
|
|
24800
|
+
delete view.refusal;
|
|
24801
|
+
delete view.question;
|
|
24802
|
+
if (record.peer.agent !== undefined)
|
|
24803
|
+
view.agent = record.peer.agent;
|
|
24804
|
+
if (named !== undefined) {
|
|
24805
|
+
view.person = { id: record.peer.person, name: named.name.slice(0, 200) };
|
|
24806
|
+
view.org = named.org;
|
|
24807
|
+
}
|
|
24808
|
+
const state = record.task.status.state;
|
|
24809
|
+
if (state === "TASK_STATE_AUTH_REQUIRED") {
|
|
24810
|
+
const mine = record.role === "executor" || me !== undefined && record.peer.person === me;
|
|
24811
|
+
const tool = signInTool(record);
|
|
24812
|
+
view.signIn = { ...tool !== undefined ? { tool } : {}, by: mine ? "you" : "them" };
|
|
24813
|
+
}
|
|
24814
|
+
if (state === "TASK_STATE_REJECTED") {
|
|
24815
|
+
const reason = refusalReason2(record);
|
|
24816
|
+
if (reason !== undefined)
|
|
24817
|
+
view.refusal = { reason };
|
|
24818
|
+
}
|
|
24819
|
+
return view;
|
|
24820
|
+
}
|
|
24821
|
+
var SIGN_IN_TEXT = "Sign-in required.";
|
|
24822
|
+
var init_task_view = __esm(() => {
|
|
24823
|
+
init_dist();
|
|
24824
|
+
});
|
|
24825
|
+
|
|
24515
24826
|
// packages/daemon/dist/realm/counterpart.js
|
|
24516
24827
|
function successors(realm, recorded) {
|
|
24517
24828
|
const chain = [recorded];
|
|
@@ -25408,6 +25719,104 @@ var init_wake_bridge = __esm(() => {
|
|
|
25408
25719
|
};
|
|
25409
25720
|
});
|
|
25410
25721
|
|
|
25722
|
+
// packages/daemon/dist/action-tokens.js
|
|
25723
|
+
import { randomBytes as randomBytes14 } from "crypto";
|
|
25724
|
+
function actionView(ctx, authority, sessionKey) {
|
|
25725
|
+
if (ctx.tier !== "admin" || sessionKey !== undefined || authority?.scope === "host")
|
|
25726
|
+
return;
|
|
25727
|
+
return { connection: ctx.id, ...authority?.dev !== undefined ? { dev: authority.dev } : {}, ...authority?.mbr !== undefined ? { mbr: authority.mbr } : {} };
|
|
25728
|
+
}
|
|
25729
|
+
function taskCancelAction(tokens, task, view) {
|
|
25730
|
+
if (view === undefined || task.role !== "requester" || task.ended)
|
|
25731
|
+
return;
|
|
25732
|
+
return { name: "cancel", token: tokens.offer(view, "cancel", `task:${task.id}`) };
|
|
25733
|
+
}
|
|
25734
|
+
|
|
25735
|
+
class ActionTokens {
|
|
25736
|
+
now;
|
|
25737
|
+
limits;
|
|
25738
|
+
#held = new Map;
|
|
25739
|
+
#offers = new Map;
|
|
25740
|
+
#perView = new Map;
|
|
25741
|
+
constructor(now, limits = { perView: ACTION_TOKENS_PER_VIEW, total: ACTION_TOKENS_TOTAL, ttlMs: ACTION_TOKEN_TTL_MS }) {
|
|
25742
|
+
this.now = now;
|
|
25743
|
+
this.limits = limits;
|
|
25744
|
+
}
|
|
25745
|
+
get size() {
|
|
25746
|
+
return this.#held.size;
|
|
25747
|
+
}
|
|
25748
|
+
offer(view, name, target) {
|
|
25749
|
+
const now = this.now();
|
|
25750
|
+
const key = viewKey(view);
|
|
25751
|
+
const offered = this.#offers.get(offerKey(key, name, target));
|
|
25752
|
+
if (offered !== undefined) {
|
|
25753
|
+
const held = this.#held.get(offered);
|
|
25754
|
+
if (held !== undefined && held.exp - now > this.limits.ttlMs / 2)
|
|
25755
|
+
return offered;
|
|
25756
|
+
}
|
|
25757
|
+
this.sweep();
|
|
25758
|
+
while ((this.#perView.get(key) ?? 0) >= this.limits.perView && this.#dropOldest((h) => h.view === key))
|
|
25759
|
+
;
|
|
25760
|
+
while (this.#held.size >= this.limits.total && this.#dropOldest(() => true))
|
|
25761
|
+
;
|
|
25762
|
+
const token = randomBytes14(32).toString("base64url");
|
|
25763
|
+
this.#held.set(token, { name, target, view: key, connection: view.connection, exp: now + this.limits.ttlMs });
|
|
25764
|
+
this.#offers.set(offerKey(key, name, target), token);
|
|
25765
|
+
this.#perView.set(key, (this.#perView.get(key) ?? 0) + 1);
|
|
25766
|
+
return token;
|
|
25767
|
+
}
|
|
25768
|
+
redeem(token, want) {
|
|
25769
|
+
const held = this.#held.get(token);
|
|
25770
|
+
if (held !== undefined)
|
|
25771
|
+
this.#delete(token, held);
|
|
25772
|
+
this.#match(held, want);
|
|
25773
|
+
}
|
|
25774
|
+
check(token, want) {
|
|
25775
|
+
this.#match(this.#held.get(token), want);
|
|
25776
|
+
}
|
|
25777
|
+
disconnected(connection) {
|
|
25778
|
+
for (const [token, held] of [...this.#held])
|
|
25779
|
+
if (held.connection === connection)
|
|
25780
|
+
this.#delete(token, held);
|
|
25781
|
+
}
|
|
25782
|
+
sweep() {
|
|
25783
|
+
const now = this.now();
|
|
25784
|
+
for (const [token, held] of [...this.#held])
|
|
25785
|
+
if (held.exp <= now)
|
|
25786
|
+
this.#delete(token, held);
|
|
25787
|
+
}
|
|
25788
|
+
#match(held, want) {
|
|
25789
|
+
const ok = held !== undefined && held.exp > this.now() && held.name === want.name && held.target === want.target && held.view === viewKey(want.view);
|
|
25790
|
+
if (!ok)
|
|
25791
|
+
throw new MessagingError("grant_invalid", "the action token is not one this daemon offered for this action, item and view, or it was spent or expired; read the view again");
|
|
25792
|
+
}
|
|
25793
|
+
#dropOldest(which) {
|
|
25794
|
+
for (const [token, held] of this.#held) {
|
|
25795
|
+
if (!which(held))
|
|
25796
|
+
continue;
|
|
25797
|
+
this.#delete(token, held);
|
|
25798
|
+
return true;
|
|
25799
|
+
}
|
|
25800
|
+
return false;
|
|
25801
|
+
}
|
|
25802
|
+
#delete(token, held) {
|
|
25803
|
+
this.#held.delete(token);
|
|
25804
|
+
const key = offerKey(held.view, held.name, held.target);
|
|
25805
|
+
if (this.#offers.get(key) === token)
|
|
25806
|
+
this.#offers.delete(key);
|
|
25807
|
+
const count = (this.#perView.get(held.view) ?? 1) - 1;
|
|
25808
|
+
if (count > 0)
|
|
25809
|
+
this.#perView.set(held.view, count);
|
|
25810
|
+
else
|
|
25811
|
+
this.#perView.delete(held.view);
|
|
25812
|
+
}
|
|
25813
|
+
}
|
|
25814
|
+
var ACTION_TOKEN_TTL_MS, ACTION_TOKENS_PER_VIEW = 256, ACTION_TOKENS_TOTAL = 4096, viewKey = (view) => JSON.stringify([view.connection, view.dev ?? null, view.mbr ?? null]), offerKey = (view, name, target) => JSON.stringify([view, name, target]);
|
|
25815
|
+
var init_action_tokens = __esm(() => {
|
|
25816
|
+
init_dist();
|
|
25817
|
+
ACTION_TOKEN_TTL_MS = 10 * 60000;
|
|
25818
|
+
});
|
|
25819
|
+
|
|
25411
25820
|
// packages/daemon/dist/consent/grants.js
|
|
25412
25821
|
class Grants {
|
|
25413
25822
|
#host;
|
|
@@ -25447,14 +25856,44 @@ class Grants {
|
|
|
25447
25856
|
const members = this.#host.participants(cid, event?.seq);
|
|
25448
25857
|
return members.includes(person) && members.includes(me.person);
|
|
25449
25858
|
})),
|
|
25859
|
+
reach: this.reach(),
|
|
25450
25860
|
inbound,
|
|
25451
25861
|
now: this.#host.now()
|
|
25452
25862
|
});
|
|
25453
25863
|
}
|
|
25864
|
+
hides(participant, org) {
|
|
25865
|
+
const slash = participant.indexOf("/");
|
|
25866
|
+
const person = slash === -1 ? participant : participant.slice(0, slash);
|
|
25867
|
+
const { me } = this.#host.joined();
|
|
25868
|
+
if (person === me.person)
|
|
25869
|
+
return false;
|
|
25870
|
+
const grant = this.personGrant(person);
|
|
25871
|
+
if (grant?.chat === "blocked")
|
|
25872
|
+
return true;
|
|
25873
|
+
if (slash !== -1 || org !== "other")
|
|
25874
|
+
return false;
|
|
25875
|
+
const relation = this.hasGrant(person) ? "contact" : "stranger";
|
|
25876
|
+
return effectivePolicy(relation, grant, [], this.#host.now(), this.reach()).chat === "blocked";
|
|
25877
|
+
}
|
|
25454
25878
|
personGrant(person) {
|
|
25455
25879
|
const grant = this.#current(grantSubjectKey({ person }));
|
|
25456
25880
|
return grant && levelsOf(grant);
|
|
25457
25881
|
}
|
|
25882
|
+
relationGrant(relation) {
|
|
25883
|
+
const grant = this.#current(grantSubjectKey({ relation }));
|
|
25884
|
+
return grant && levelsOf(grant);
|
|
25885
|
+
}
|
|
25886
|
+
reach() {
|
|
25887
|
+
return reachChoices(this.relationGrant("outside"), this.relationGrant("org"));
|
|
25888
|
+
}
|
|
25889
|
+
relationRecord(relation) {
|
|
25890
|
+
const key = grantSubjectKey({ relation });
|
|
25891
|
+
return { grant: this.#store.grant(key) ?? null, review: this.#store.grantReviews().find(({ subject }) => subject === key) ?? null };
|
|
25892
|
+
}
|
|
25893
|
+
relationReview(relation) {
|
|
25894
|
+
const row = this.#store.grantReviews().find(({ subject }) => subject === grantSubjectKey({ relation }));
|
|
25895
|
+
return row?.levels;
|
|
25896
|
+
}
|
|
25458
25897
|
conversationGrants(conversations) {
|
|
25459
25898
|
return conversations.flatMap((conv) => {
|
|
25460
25899
|
const grant = this.#current(grantSubjectKey({ conv }));
|
|
@@ -25476,8 +25915,8 @@ class Grants {
|
|
|
25476
25915
|
}
|
|
25477
25916
|
list(p) {
|
|
25478
25917
|
const only = p.subject === undefined ? undefined : this.#subjectKey(p.subject, false);
|
|
25479
|
-
const grants = this.#all().filter(({ subject }) => only === undefined || subject === only).map(({ grant }) => this.#view(grant)).sort((a, b) => b.ts - a.ts || (a.id < b.id ? 1 : -1));
|
|
25480
|
-
const review = this.#store.grantReviews().filter(({ subject }) => only === undefined || subject === only).map(({ subject, levels, at }) => ({ subject: this.#subjectView(subject), ...levels, at }));
|
|
25918
|
+
const grants = this.#all().filter(({ subject }) => !isRelationKey(subject)).filter(({ subject }) => only === undefined || subject === only).map(({ grant }) => this.#view(grant)).sort((a, b) => b.ts - a.ts || (a.id < b.id ? 1 : -1));
|
|
25919
|
+
const review = this.#store.grantReviews().filter(({ subject }) => !isRelationKey(subject)).filter(({ subject }) => only === undefined || subject === only).map(({ subject, levels, at }) => ({ subject: this.#subjectView(subject), ...levels, at }));
|
|
25481
25920
|
return { grants, review };
|
|
25482
25921
|
}
|
|
25483
25922
|
set(p, opts = {}) {
|
|
@@ -25505,6 +25944,22 @@ class Grants {
|
|
|
25505
25944
|
delete levels.trustedUntil;
|
|
25506
25945
|
return this.set({ subject: { person }, ...levels }, opts);
|
|
25507
25946
|
}
|
|
25947
|
+
setRelations(changes) {
|
|
25948
|
+
if (changes.length === 0)
|
|
25949
|
+
return;
|
|
25950
|
+
const { identity, realm, me } = this.#host.joined();
|
|
25951
|
+
const now = this.#host.now();
|
|
25952
|
+
const grants = changes.map(({ subject, levels }) => signConsentGrant(identity, { realm: realm.realm, person: me.person, subject, levels, now }));
|
|
25953
|
+
this.#host.tx(() => {
|
|
25954
|
+
for (const grant of grants) {
|
|
25955
|
+
const key = grantSubjectKey(grant.subject);
|
|
25956
|
+
this.#store.putGrant(key, grant);
|
|
25957
|
+
this.#store.deleteGrantReview(key);
|
|
25958
|
+
}
|
|
25959
|
+
this.#sync("grant", grants);
|
|
25960
|
+
});
|
|
25961
|
+
this.#host.log("grant.set", { subject: "relation", grants: grants.length });
|
|
25962
|
+
}
|
|
25508
25963
|
check(p) {
|
|
25509
25964
|
const { identity, realm, me } = this.#host.joined();
|
|
25510
25965
|
const now = this.#host.now();
|
|
@@ -25531,7 +25986,7 @@ class Grants {
|
|
|
25531
25986
|
}
|
|
25532
25987
|
const key = grantSubjectKey(grant.subject);
|
|
25533
25988
|
const held = this.#held(key);
|
|
25534
|
-
if (!held && this.#store.grantCount() >= LIMITS.grantsPerPerson) {
|
|
25989
|
+
if (!held && !isRelationKey(key) && this.#store.grantCount() >= LIMITS.grantsPerPerson) {
|
|
25535
25990
|
this.#host.log("grant.sync.skipped", { id: env.id, code: "rate_limited" });
|
|
25536
25991
|
continue;
|
|
25537
25992
|
}
|
|
@@ -25665,10 +26120,17 @@ class Grants {
|
|
|
25665
26120
|
return grantSubjectKey(target);
|
|
25666
26121
|
}
|
|
25667
26122
|
#subjectOf(key) {
|
|
25668
|
-
|
|
26123
|
+
if (key.startsWith("person:"))
|
|
26124
|
+
return { person: key.slice("person:".length) };
|
|
26125
|
+
if (isRelationKey(key))
|
|
26126
|
+
return { relation: key.slice("relation:".length) };
|
|
26127
|
+
return { conv: key.slice("conv:".length) };
|
|
25669
26128
|
}
|
|
25670
26129
|
#subjectView(key) {
|
|
25671
|
-
|
|
26130
|
+
const subject = this.#subjectOf(key);
|
|
26131
|
+
if ("relation" in subject)
|
|
26132
|
+
throw new MessagingError("internal", "a relation grant has no grant view");
|
|
26133
|
+
return subject;
|
|
25672
26134
|
}
|
|
25673
26135
|
#view(grant) {
|
|
25674
26136
|
return {
|
|
@@ -25681,6 +26143,9 @@ class Grants {
|
|
|
25681
26143
|
};
|
|
25682
26144
|
}
|
|
25683
26145
|
}
|
|
26146
|
+
function isRelationKey(key) {
|
|
26147
|
+
return key.startsWith("relation:");
|
|
26148
|
+
}
|
|
25684
26149
|
function levelsFrom(p, now) {
|
|
25685
26150
|
const trustedUntil = p.trustedUntil ?? (p.trust ? now + LIMITS.trustDefaultMs : undefined);
|
|
25686
26151
|
return levelsOf({ chat: p.chat, agentMail: p.agentMail, ask: p.ask, trustedUntil, discoverable: p.discoverable });
|
|
@@ -25816,7 +26281,7 @@ class Cards {
|
|
|
25816
26281
|
seq,
|
|
25817
26282
|
now,
|
|
25818
26283
|
...grant !== undefined && typeof held?.id === "string" ? { grantId: held.id } : {},
|
|
25819
|
-
can: cardCan(relation, grant, now)
|
|
26284
|
+
can: cardCan(relation, grant, now, this.#host.reach?.() ?? {})
|
|
25820
26285
|
});
|
|
25821
26286
|
for (const device of realm.known(person).devices) {
|
|
25822
26287
|
this.#host.route(this.#host.seal(device.machine, cardMessage2({ card }, "card")));
|
|
@@ -25943,7 +26408,10 @@ class ConversationRequests {
|
|
|
25943
26408
|
return this.#ids().map((id) => this.get(id)).sort((a, b) => a.at - b.at || a.id.localeCompare(b.id));
|
|
25944
26409
|
}
|
|
25945
26410
|
waitingOnThem() {
|
|
25946
|
-
|
|
26411
|
+
const now = this.host.now(), rows = this.list().filter((r) => r.direction === "outgoing");
|
|
26412
|
+
const submitted = rows.filter((r) => r.state === "submitted" && r.request.descriptor.expiresAt > now).map((r) => ({ id: r.id, kind: "contact", to: r.person, name: r.person, at: r.at, cid: r.request.descriptor.cid }));
|
|
26413
|
+
const resend = rows.filter((r) => r.state === "resend" && r.at + LIMITS.ttlMs > now && !rows.some((n) => n.person === r.person && n.request && n.at >= r.at)).map((r) => ({ id: r.id, kind: "contact", to: r.person, name: r.person, at: r.at, cid: dmConvId(this.host.person, r.person), resend: true }));
|
|
26414
|
+
return [...submitted, ...resend];
|
|
25947
26415
|
}
|
|
25948
26416
|
cutoverLegacy(outgoing = []) {
|
|
25949
26417
|
this.host.store.tx(() => {
|
|
@@ -26015,6 +26483,8 @@ class ConversationRequests {
|
|
|
26015
26483
|
throw new MessagingError("expired", "request has expired");
|
|
26016
26484
|
if (this.host.chat(d.from) === "blocked")
|
|
26017
26485
|
throw new MessagingError("declined", "request declined");
|
|
26486
|
+
if (this.host.refuses?.(d.from))
|
|
26487
|
+
throw new MessagingError("refused", "this person accepts no message requests from outside their org");
|
|
26018
26488
|
return this.host.store.tx(() => {
|
|
26019
26489
|
const old = this.get(d.id);
|
|
26020
26490
|
if (old) {
|
|
@@ -26180,6 +26650,7 @@ class Requests {
|
|
|
26180
26650
|
resolve: (email) => this.#resolve(email),
|
|
26181
26651
|
current: (person) => h.current?.([person]) ?? Promise.resolve(),
|
|
26182
26652
|
chat: (person) => h.grants.personGrant(person)?.chat,
|
|
26653
|
+
refuses: (person) => h.grants.decide(person, { kind: "request" }).action === "refuse",
|
|
26183
26654
|
grantChat: (person, chat) => {
|
|
26184
26655
|
h.grants.setPerson(person, { chat }, { card: false });
|
|
26185
26656
|
},
|
|
@@ -26246,7 +26717,7 @@ class Requests {
|
|
|
26246
26717
|
}, proof, body);
|
|
26247
26718
|
}
|
|
26248
26719
|
});
|
|
26249
|
-
this.#lifecycle.cutoverLegacy();
|
|
26720
|
+
this.#lifecycle.cutoverLegacy(h.store.legacyOutgoingRequests(h.now()));
|
|
26250
26721
|
return this.#lifecycle;
|
|
26251
26722
|
}
|
|
26252
26723
|
#mailbox() {
|
|
@@ -26327,12 +26798,17 @@ class Requests {
|
|
|
26327
26798
|
return true;
|
|
26328
26799
|
if (payload.stage === "held")
|
|
26329
26800
|
this.host.store.setKv(`conv.request.held.${sent.request}`, "true");
|
|
26801
|
+
if (payload.stage === "delivered" || payload.stage === "read") {
|
|
26802
|
+
if (!this.host.store.getKv(`conv.request.outdated.${sent.request}`))
|
|
26803
|
+
this.host.log("request.outdated", { id: sent.request });
|
|
26804
|
+
this.host.store.setKv(`conv.request.outdated.${sent.request}`, "true");
|
|
26805
|
+
}
|
|
26330
26806
|
return true;
|
|
26331
26807
|
}
|
|
26332
26808
|
async waitingOnThem() {
|
|
26333
|
-
return this.#current().waitingOnThem().map((row) => ({
|
|
26809
|
+
return this.#current().waitingOnThem().map(({ resend, ...row }) => ({
|
|
26334
26810
|
...row,
|
|
26335
|
-
state: this.host.store.getKv(`conv.request.held.${row.id}`) === "true" ? "held" : "pending-delivery"
|
|
26811
|
+
state: resend ? "resend" : this.host.store.getKv(`conv.request.outdated.${row.id}`) === "true" ? "outdated" : this.host.store.getKv(`conv.request.held.${row.id}`) === "true" ? "held" : "pending-delivery"
|
|
26336
26812
|
}));
|
|
26337
26813
|
}
|
|
26338
26814
|
get(id) {
|
|
@@ -26449,6 +26925,91 @@ var init_requests2 = __esm(() => {
|
|
|
26449
26925
|
init_request_importer();
|
|
26450
26926
|
});
|
|
26451
26927
|
|
|
26928
|
+
// packages/daemon/dist/consent/reach.js
|
|
26929
|
+
function revision(grants, choices) {
|
|
26930
|
+
const records = Object.fromEntries(GROUPS2.map((group) => [group, grants.relationRecord(RELATION2[group])]));
|
|
26931
|
+
return sha256(canonicalize({ records, choices })).slice(0, 32);
|
|
26932
|
+
}
|
|
26933
|
+
function reachSnapshot(grants) {
|
|
26934
|
+
const choices = grants.reach();
|
|
26935
|
+
const lapsed = reachChoices(grants.relationReview("outside"), grants.relationReview("org"));
|
|
26936
|
+
return {
|
|
26937
|
+
revision: revision(grants, choices),
|
|
26938
|
+
available: true,
|
|
26939
|
+
groups: REACH_QUESTIONS.map((question) => ({
|
|
26940
|
+
id: question.id,
|
|
26941
|
+
label: question.label,
|
|
26942
|
+
options: question.options.map((option) => ({ ...option })),
|
|
26943
|
+
value: choices[question.id] ?? null,
|
|
26944
|
+
...lapsed[question.id] !== undefined ? { lapsed: lapsed[question.id] } : {}
|
|
26945
|
+
}))
|
|
26946
|
+
};
|
|
26947
|
+
}
|
|
26948
|
+
function changesOf(grants, p) {
|
|
26949
|
+
if (p.revision !== revision(grants, grants.reach())) {
|
|
26950
|
+
throw new MessagingError("conflict", "your reach choices changed since they were read; read them again and choose again");
|
|
26951
|
+
}
|
|
26952
|
+
return GROUPS2.flatMap((group) => p.changes[group] === undefined ? [] : [[group, p.changes[group]]]);
|
|
26953
|
+
}
|
|
26954
|
+
function reachSet(grants, p) {
|
|
26955
|
+
const changes = changesOf(grants, p);
|
|
26956
|
+
grants.setRelations(changes.map(([group, value]) => {
|
|
26957
|
+
const { levels } = reachGrant(group, value);
|
|
26958
|
+
return { subject: { relation: RELATION2[group] }, levels };
|
|
26959
|
+
}));
|
|
26960
|
+
return reachSnapshot(grants);
|
|
26961
|
+
}
|
|
26962
|
+
function reachPreview(grants, p) {
|
|
26963
|
+
const changes = changesOf(grants, p);
|
|
26964
|
+
if (changes.length === 0)
|
|
26965
|
+
return "Nothing changes.";
|
|
26966
|
+
return changes.map(([group, value]) => SENTENCES[group][value]).join(" ");
|
|
26967
|
+
}
|
|
26968
|
+
var RELATION2, GROUPS2, SENTENCES;
|
|
26969
|
+
var init_reach = __esm(() => {
|
|
26970
|
+
init_dist();
|
|
26971
|
+
RELATION2 = { message: "outside", ask: "org" };
|
|
26972
|
+
GROUPS2 = ["message", "ask"];
|
|
26973
|
+
SENTENCES = {
|
|
26974
|
+
message: {
|
|
26975
|
+
open: "Anyone outside your org may message you.",
|
|
26976
|
+
ask: "People outside your org send you a message request first.",
|
|
26977
|
+
never: "No one outside your org may message you or send you a request, except people you have let message you."
|
|
26978
|
+
},
|
|
26979
|
+
ask: {
|
|
26980
|
+
open: "Your teammates' agents may ask yours without asking you; their requests run guarded.",
|
|
26981
|
+
ask: "You answer each request from a teammate's agent to yours first.",
|
|
26982
|
+
never: "Your teammates' agents may not ask yours."
|
|
26983
|
+
}
|
|
26984
|
+
};
|
|
26985
|
+
});
|
|
26986
|
+
|
|
26987
|
+
// packages/daemon/dist/conv-caps.js
|
|
26988
|
+
async function ensureCurrent(link, own, persons, handle) {
|
|
26989
|
+
const others = [...new Set(persons)].filter((p) => p !== own);
|
|
26990
|
+
if (!others.length)
|
|
26991
|
+
return;
|
|
26992
|
+
let outdated;
|
|
26993
|
+
try {
|
|
26994
|
+
outdated = (await link?.convCaps?.(others))?.outdated ?? [];
|
|
26995
|
+
} catch {
|
|
26996
|
+
return;
|
|
26997
|
+
}
|
|
26998
|
+
if (!outdated.length)
|
|
26999
|
+
return;
|
|
27000
|
+
const handles = await Promise.all(outdated.map(async (p) => {
|
|
27001
|
+
try {
|
|
27002
|
+
return await handle(p);
|
|
27003
|
+
} catch {
|
|
27004
|
+
return p;
|
|
27005
|
+
}
|
|
27006
|
+
}));
|
|
27007
|
+
throw new MessagingError("peer_outdated", `${handles.join(", ")} ${handles.length === 1 ? "needs" : "need"} to update Messaging`);
|
|
27008
|
+
}
|
|
27009
|
+
var init_conv_caps = __esm(() => {
|
|
27010
|
+
init_dist();
|
|
27011
|
+
});
|
|
27012
|
+
|
|
26452
27013
|
// packages/daemon/dist/consent/sentence.js
|
|
26453
27014
|
function grantSentence(p, me, now) {
|
|
26454
27015
|
const levels = levelsFrom(p, now);
|
|
@@ -26487,6 +27048,41 @@ var init_sentence = __esm(() => {
|
|
|
26487
27048
|
init_grants();
|
|
26488
27049
|
});
|
|
26489
27050
|
|
|
27051
|
+
// packages/daemon/dist/wake-dms.js
|
|
27052
|
+
function teammateDms(conversations, self) {
|
|
27053
|
+
const member = (state, person) => state.participants.some((p) => p.person === person && p.state === "member");
|
|
27054
|
+
const seated = (state, person) => state.participants.some((p) => p.p === person && p.state === "member");
|
|
27055
|
+
const channels = conversations.flatMap((c) => c.state?.kind === "channel" && seated(c.state, self) ? [c.state] : []);
|
|
27056
|
+
const shares = new Map;
|
|
27057
|
+
const shared = (person) => {
|
|
27058
|
+
let found = shares.get(person);
|
|
27059
|
+
if (found === undefined)
|
|
27060
|
+
shares.set(person, found = channels.some((state) => seated(state, person)));
|
|
27061
|
+
return found;
|
|
27062
|
+
};
|
|
27063
|
+
const found = [];
|
|
27064
|
+
for (const { cid, state } of conversations) {
|
|
27065
|
+
if (state?.kind !== "dm" || !member(state, self))
|
|
27066
|
+
continue;
|
|
27067
|
+
const others = [...new Set(state.participants.filter((p) => p.person !== self).map((p) => p.person))];
|
|
27068
|
+
if (others.length !== 1)
|
|
27069
|
+
continue;
|
|
27070
|
+
const person = others[0];
|
|
27071
|
+
if (!member(state, person) || !shared(person))
|
|
27072
|
+
continue;
|
|
27073
|
+
found.push({ cid, person });
|
|
27074
|
+
}
|
|
27075
|
+
return found;
|
|
27076
|
+
}
|
|
27077
|
+
function wakeArmable(grants, person, org) {
|
|
27078
|
+
const chat = grants.personGrant(person)?.chat;
|
|
27079
|
+
if (chat === "blocked" || chat === "muted")
|
|
27080
|
+
return false;
|
|
27081
|
+
if (grants.hides(person, org))
|
|
27082
|
+
return false;
|
|
27083
|
+
return true;
|
|
27084
|
+
}
|
|
27085
|
+
|
|
26490
27086
|
// packages/daemon/dist/daemon.js
|
|
26491
27087
|
async function startDaemon(options) {
|
|
26492
27088
|
const daemon = await Daemon.start(options);
|
|
@@ -26498,6 +27094,7 @@ async function startDaemon(options) {
|
|
|
26498
27094
|
status: () => daemon.status(),
|
|
26499
27095
|
conversations: () => daemon.conversations(),
|
|
26500
27096
|
conversationsReady: () => daemon.conversationsReady(),
|
|
27097
|
+
actionTokens: () => daemon.actionTokens(),
|
|
26501
27098
|
sweep: () => daemon.sweep(),
|
|
26502
27099
|
close: () => daemon.close()
|
|
26503
27100
|
};
|
|
@@ -26562,11 +27159,14 @@ class Daemon {
|
|
|
26562
27159
|
#cards;
|
|
26563
27160
|
#requests;
|
|
26564
27161
|
#notices;
|
|
27162
|
+
#named = new Map;
|
|
27163
|
+
#questions = new Map;
|
|
26565
27164
|
#jellyHost;
|
|
26566
27165
|
#attached = false;
|
|
26567
27166
|
#wakeArmed = false;
|
|
26568
27167
|
#snapshots;
|
|
26569
27168
|
#jellyAuth;
|
|
27169
|
+
#actions;
|
|
26570
27170
|
#state = "starting";
|
|
26571
27171
|
#sync;
|
|
26572
27172
|
#pending = [];
|
|
@@ -26602,6 +27202,7 @@ class Daemon {
|
|
|
26602
27202
|
this.#jellyHttp = options.jellyHttp;
|
|
26603
27203
|
this.#loginWait = options.loginWait;
|
|
26604
27204
|
this.#jellyHost = options.jellyHost;
|
|
27205
|
+
this.#actions = new ActionTokens(() => this.#now());
|
|
26605
27206
|
this.#config = readConfig(paths.config);
|
|
26606
27207
|
this.#configStamp = configStamp(paths.config);
|
|
26607
27208
|
this.closed = new Promise((resolve) => this.#settleClosed = resolve);
|
|
@@ -26619,9 +27220,10 @@ class Daemon {
|
|
|
26619
27220
|
put: (key, value) => store.setKv(key, value),
|
|
26620
27221
|
fresh: (people) => this.#withFresh(people, () => {}),
|
|
26621
27222
|
refuseSecrets: (value) => this.#refuseSecrets(value, "conv.send", "conversation line"),
|
|
26622
|
-
blocked: (
|
|
27223
|
+
blocked: (participant, org) => this.#grants.hides(participant, org),
|
|
26623
27224
|
current: (persons) => this.#ensureCurrent(persons),
|
|
26624
|
-
outgoingRequests: () => this.#requests.waitingOnThem()
|
|
27225
|
+
outgoingRequests: () => this.#requests.waitingOnThem(),
|
|
27226
|
+
task: (id, role, ctx, authority) => this.#personTaskEntry(id, role, ctx, authority)
|
|
26625
27227
|
});
|
|
26626
27228
|
this.#conversationInbox = new ConversationInbox({
|
|
26627
27229
|
engine: () => this.#conversationEngine(),
|
|
@@ -26706,6 +27308,7 @@ class Daemon {
|
|
|
26706
27308
|
joined: () => this.#joined(),
|
|
26707
27309
|
relation: (person) => this.#grants.relation(person),
|
|
26708
27310
|
personGrant: (person) => this.#grants.personGrant(person),
|
|
27311
|
+
reach: () => this.#grants.reach(),
|
|
26709
27312
|
prefetch: (person) => this.#grants.prefetch(person),
|
|
26710
27313
|
fresh: async (persons, fn) => {
|
|
26711
27314
|
await this.#withFresh(persons, () => this.#tx(fn), { first: false });
|
|
@@ -27051,7 +27654,7 @@ class Daemon {
|
|
|
27051
27654
|
},
|
|
27052
27655
|
"device.list": () => {
|
|
27053
27656
|
const result = this.#directory?.list() ?? this.#rosterDevices();
|
|
27054
|
-
return { devices: result.devices.map((d) => d.self && this.#away() ? { ...d, away: true } : d) };
|
|
27657
|
+
return { devices: result.devices.map((d) => this.#withFeatures(d.self && this.#away() ? { ...d, away: true } : d)) };
|
|
27055
27658
|
},
|
|
27056
27659
|
"device.remove": (p) => this.#homeDirectory("device.remove").remove(p),
|
|
27057
27660
|
"device.link": (p) => this.#homeDirectory("device.link").link(p),
|
|
@@ -27095,10 +27698,10 @@ class Daemon {
|
|
|
27095
27698
|
"inbox.ack": (p, ctx) => this.#ack(this.#bound(p, ctx), ctx),
|
|
27096
27699
|
"task.create": (p, ctx) => this.#sendTask({ ...this.#bound(p, ctx), kind: "task" }),
|
|
27097
27700
|
"task.continue": (p, ctx) => this.#sendTask({ ...this.#bound(p, ctx), kind: "message" }),
|
|
27098
|
-
"task.list": (p, ctx) => this.#listTasks(this.#bound(p, ctx)),
|
|
27099
|
-
"task.get": (p, ctx) => this.#getTask(this.#bound(p, ctx)),
|
|
27100
|
-
"task.update": (p, ctx) => this.#updateTask(this.#bound(p, ctx)),
|
|
27101
|
-
"task.cancel": (p, ctx) => this.#cancelTask(this.#bound(p, ctx)),
|
|
27701
|
+
"task.list": (p, ctx) => this.#listTasks(this.#bound(p, ctx), ctx.tier === "admin"),
|
|
27702
|
+
"task.get": (p, ctx) => this.#getTask(this.#bound(p, ctx), ctx.tier === "admin"),
|
|
27703
|
+
"task.update": (p, ctx) => this.#updateTask(this.#bound(p, ctx), ctx.tier === "admin"),
|
|
27704
|
+
"task.cancel": (p, ctx, authority) => p.token === undefined ? this.#cancelTask(this.#bound({ ...p, key: p.key }, ctx), ctx.tier === "admin") : this.#cancelByToken(p, p.token, this.#tokenView("task.cancel", ctx, authority)),
|
|
27102
27705
|
"events.subscribe": (p, ctx) => {
|
|
27103
27706
|
for (const key of p.keys)
|
|
27104
27707
|
this.#bound({ key }, ctx);
|
|
@@ -27128,9 +27731,24 @@ class Daemon {
|
|
|
27128
27731
|
this.#grants.check(p);
|
|
27129
27732
|
return { sentence: grantSentence(p, this.#joined().me.person, this.#now()) };
|
|
27130
27733
|
},
|
|
27131
|
-
"
|
|
27132
|
-
"
|
|
27133
|
-
"
|
|
27734
|
+
"reach.get": () => reachSnapshot(this.#grants),
|
|
27735
|
+
"reach.set": (p) => reachSet(this.#grants, p),
|
|
27736
|
+
"reach.set.preview": (p) => ({ sentence: reachPreview(this.#grants, p) }),
|
|
27737
|
+
"request.list": async (_p, ctx, authority) => {
|
|
27738
|
+
const requests = [...this.#holds.list().requests, ...await this.#requests.list(), ...this.#conversationInvitations.list()];
|
|
27739
|
+
const view = actionView(ctx, authority, undefined);
|
|
27740
|
+
return { requests: view === undefined ? requests : requests.map((request) => this.#withActions(request, view)) };
|
|
27741
|
+
},
|
|
27742
|
+
"request.answer": (p, ctx, authority) => {
|
|
27743
|
+
this.#requestToken("request.answer", p, ctx, authority, "redeem");
|
|
27744
|
+
const { token: _token, ...answer } = p;
|
|
27745
|
+
return this.#conversationInvitations.holds(answer.id) ? this.#conversationInvitations.answer(answer.id, answer.answer, answer.all) : this.#requests.holds(answer.id) ? this.#requests.answer(answer) : this.#holds.answer(answer);
|
|
27746
|
+
},
|
|
27747
|
+
"request.answer.preview": (p, ctx, authority) => {
|
|
27748
|
+
this.#requestToken("request.answer.preview", p, ctx, authority, "check");
|
|
27749
|
+
const { token: _token, ...answer } = p;
|
|
27750
|
+
return this.#answerPreview(answer);
|
|
27751
|
+
},
|
|
27134
27752
|
"contact.request": (p) => this.#requests.request(p),
|
|
27135
27753
|
"contact.request.preview": (p) => this.#requests.preview(p),
|
|
27136
27754
|
"card.get": async (p) => {
|
|
@@ -27147,6 +27765,9 @@ class Daemon {
|
|
|
27147
27765
|
"notice.subscribe": (p, ctx, authority) => this.#notices.subscribe(ctx, p, authority?.scope === "host" ? authority.expiresAt : undefined),
|
|
27148
27766
|
"notice.list": (p) => this.#notices.list(p),
|
|
27149
27767
|
"notice.ack": (p) => this.#notices.ack(p),
|
|
27768
|
+
"task.question": (p, ctx) => this.#setQuestion(this.#bound(p, ctx)),
|
|
27769
|
+
"tasks.list": (p, ctx, authority) => this.#personTasks(p, actionView(ctx, authority, undefined)),
|
|
27770
|
+
"tasks.get": (p, ctx, authority) => this.#personTaskGet(p, actionView(ctx, authority, undefined)),
|
|
27150
27771
|
"device.remove.preview": (p) => this.#removePreview(p),
|
|
27151
27772
|
...this.#conversationIpc.handlers()
|
|
27152
27773
|
};
|
|
@@ -27214,8 +27835,9 @@ class Daemon {
|
|
|
27214
27835
|
this.#publish(event);
|
|
27215
27836
|
}
|
|
27216
27837
|
#emitTask(row, origin, update) {
|
|
27217
|
-
const record = this.#withFollowUps(boundedRecord(row.record, TASK_BYTES));
|
|
27838
|
+
const record = this.#taskView(this.#withFollowUps(boundedRecord(row.record, TASK_BYTES)));
|
|
27218
27839
|
this.#emit({ type: "task", key: row.record.key, record, origin, ...update ? { update: structuredClone(update) } : {} });
|
|
27840
|
+
this.#conversationIpc.taskChanged({ id: row.record.task.id, role: row.record.role });
|
|
27219
27841
|
}
|
|
27220
27842
|
#overBudget(before, after) {
|
|
27221
27843
|
const size = jsonBytes(after);
|
|
@@ -27420,6 +28042,7 @@ class Daemon {
|
|
|
27420
28042
|
#disconnected(ctx) {
|
|
27421
28043
|
if (this.#closed)
|
|
27422
28044
|
return;
|
|
28045
|
+
this.#actions.disconnected(ctx.id);
|
|
27423
28046
|
this.#notices.disconnected(ctx.id);
|
|
27424
28047
|
let changed = false;
|
|
27425
28048
|
for (const [key, lapsed] of [...this.#lapsed])
|
|
@@ -28335,6 +28958,8 @@ ${row.receipt ? "receipt" : "envelope"}`;
|
|
|
28335
28958
|
};
|
|
28336
28959
|
this.#store.putTask(row);
|
|
28337
28960
|
this.#emitTask(row, "peer");
|
|
28961
|
+
if (sender.person !== this.#joined().me.person)
|
|
28962
|
+
this.#notices.task({ taskId, person: sender.person, at: now });
|
|
28338
28963
|
} else if (executor) {
|
|
28339
28964
|
executor.record.task.history = [...executor.record.task.history ?? [], message];
|
|
28340
28965
|
executor.record.updatedAt = now;
|
|
@@ -28407,6 +29032,7 @@ ${row.receipt ? "receipt" : "envelope"}`;
|
|
|
28407
29032
|
if (isTerminal(row.record.task.status.state))
|
|
28408
29033
|
return this.#dropped("invalid_state", env.id);
|
|
28409
29034
|
const status = { state: "TASK_STATE_CANCELED", timestamp: iso3(now) };
|
|
29035
|
+
this.#questions.delete(row.record.task.id);
|
|
28410
29036
|
row.record.task.status = status;
|
|
28411
29037
|
row.record.updatedAt = now;
|
|
28412
29038
|
this.#store.putTask(row);
|
|
@@ -28488,11 +29114,11 @@ ${row.receipt ? "receipt" : "envelope"}`;
|
|
|
28488
29114
|
}
|
|
28489
29115
|
throw new MessagingError("not_found", "no such task for this session");
|
|
28490
29116
|
}
|
|
28491
|
-
#getTask(p) {
|
|
29117
|
+
async#getTask(p, owner) {
|
|
28492
29118
|
for (const role of ["requester", "executor"]) {
|
|
28493
29119
|
const row = this.#store.task(p.taskId, role);
|
|
28494
29120
|
if (row && row.record.key === p.key)
|
|
28495
|
-
return this.#withFollowUps(row.record);
|
|
29121
|
+
return this.#withQuestion(await this.#viewNamed(this.#withFollowUps(row.record)), owner);
|
|
28496
29122
|
}
|
|
28497
29123
|
throw new MessagingError("not_found", "no such task for this session");
|
|
28498
29124
|
}
|
|
@@ -28511,7 +29137,7 @@ ${row.receipt ? "receipt" : "envelope"}`;
|
|
|
28511
29137
|
}));
|
|
28512
29138
|
return { ...record, followUps };
|
|
28513
29139
|
}
|
|
28514
|
-
#listTasks(p) {
|
|
29140
|
+
async#listTasks(p, owner) {
|
|
28515
29141
|
const limit = p.limit ?? 50;
|
|
28516
29142
|
const { rows, total, more } = this.#store.listTasks({
|
|
28517
29143
|
key: p.key,
|
|
@@ -28535,13 +29161,14 @@ ${row.receipt ? "receipt" : "envelope"}`;
|
|
|
28535
29161
|
}
|
|
28536
29162
|
const last = tasks.at(-1);
|
|
28537
29163
|
const truncated = tasks.length < rows.length;
|
|
29164
|
+
await Promise.all([...new Set(tasks.map((t) => t.peer.person))].map((person) => this.#nameOf(person)));
|
|
28538
29165
|
return {
|
|
28539
|
-
tasks,
|
|
29166
|
+
tasks: tasks.map((t) => this.#withQuestion(this.#taskView(t), owner)),
|
|
28540
29167
|
total,
|
|
28541
29168
|
...(more || truncated) && last ? { nextCursor: encodeCursor({ updatedAt: last.updatedAt, taskId: last.task.id, role: last.role }) } : {}
|
|
28542
29169
|
};
|
|
28543
29170
|
}
|
|
28544
|
-
async#updateTask(p) {
|
|
29171
|
+
async#updateTask(p, owner) {
|
|
28545
29172
|
this.#joined();
|
|
28546
29173
|
const row = this.#ownTask(p.key, p.taskId, "executor");
|
|
28547
29174
|
this.#refuseSecrets(p, "task.update", "task update");
|
|
@@ -28555,7 +29182,7 @@ ${row.receipt ? "receipt" : "envelope"}`;
|
|
|
28555
29182
|
const now = this.#now();
|
|
28556
29183
|
const status = statusChange ? {
|
|
28557
29184
|
state: p.state,
|
|
28558
|
-
...p.message !== undefined ? { message: agentMessage(p.message.parts, task.id, task.contextId, now) } : {},
|
|
29185
|
+
...p.signIn !== undefined ? { message: signInMessage(agentMessage(p.message?.parts ?? [{ text: SIGN_IN_TEXT }], task.id, task.contextId, now), p.signIn.tool) } : p.message !== undefined ? { message: agentMessage(p.message.parts, task.id, task.contextId, now) } : {},
|
|
28559
29186
|
timestamp: iso3(now)
|
|
28560
29187
|
} : undefined;
|
|
28561
29188
|
const after = structuredClone(row.record);
|
|
@@ -28581,6 +29208,7 @@ ${row.receipt ? "receipt" : "envelope"}`;
|
|
|
28581
29208
|
}
|
|
28582
29209
|
if (status) {
|
|
28583
29210
|
const env = this.#seal("task.status", requester, session, { taskId: task.id, contextId, status }, { replyTo: request });
|
|
29211
|
+
this.#questions.delete(task.id);
|
|
28584
29212
|
task.status = status;
|
|
28585
29213
|
row.record.updatedAt = now;
|
|
28586
29214
|
this.#emitTask(row, "local", { status });
|
|
@@ -28590,12 +29218,63 @@ ${row.receipt ? "receipt" : "envelope"}`;
|
|
|
28590
29218
|
this.#store.putTask(row);
|
|
28591
29219
|
});
|
|
28592
29220
|
await this.#settle(ids);
|
|
28593
|
-
return boundedRecord(this.#store.task(p.taskId, "executor")?.record ?? row.record, TASK_BYTES);
|
|
29221
|
+
return this.#withQuestion(await this.#viewNamed(boundedRecord(this.#store.task(p.taskId, "executor")?.record ?? row.record, TASK_BYTES)), owner);
|
|
28594
29222
|
}
|
|
28595
|
-
async#cancelTask(p) {
|
|
29223
|
+
async#cancelTask(p, owner) {
|
|
28596
29224
|
this.#joined();
|
|
28597
|
-
|
|
28598
|
-
|
|
29225
|
+
return this.#cancelRow(this.#ownTask(p.key, p.taskId, "requester"), p, owner);
|
|
29226
|
+
}
|
|
29227
|
+
#tokenView(method, ctx, authority) {
|
|
29228
|
+
const view = actionView(ctx, authority, undefined);
|
|
29229
|
+
if (view === undefined) {
|
|
29230
|
+
this.#log("warn", "action.token.refused", { method, reason: "not the human tier" });
|
|
29231
|
+
throw new MessagingError("forbidden", `${method} takes an action token only on the human tier`);
|
|
29232
|
+
}
|
|
29233
|
+
return view;
|
|
29234
|
+
}
|
|
29235
|
+
#requestToken(method, p, ctx, authority, use) {
|
|
29236
|
+
if (p.token === undefined) {
|
|
29237
|
+
if (this.#jellyHost === undefined)
|
|
29238
|
+
return;
|
|
29239
|
+
this.#log("warn", "action.token.refused", { method, reason: "missing" });
|
|
29240
|
+
throw new MessagingError("grant_required", `${method} under a Jelly host needs the request's action token from request.list`);
|
|
29241
|
+
}
|
|
29242
|
+
this.#useToken(method, p.token, { name: p.answer, target: `request:${p.id}`, view: this.#tokenView(method, ctx, authority) }, use);
|
|
29243
|
+
}
|
|
29244
|
+
#useToken(method, token, want, use) {
|
|
29245
|
+
try {
|
|
29246
|
+
if (use === "redeem")
|
|
29247
|
+
this.#actions.redeem(token, want);
|
|
29248
|
+
else
|
|
29249
|
+
this.#actions.check(token, want);
|
|
29250
|
+
} catch (error) {
|
|
29251
|
+
this.#log("warn", "action.token.refused", { method, reason: "no matching live token" });
|
|
29252
|
+
throw error;
|
|
29253
|
+
}
|
|
29254
|
+
}
|
|
29255
|
+
actionTokens() {
|
|
29256
|
+
return this.#actions.size;
|
|
29257
|
+
}
|
|
29258
|
+
#withActions(request, view) {
|
|
29259
|
+
if (request.kind === "contact" && request.state !== "pending")
|
|
29260
|
+
return request;
|
|
29261
|
+
const names = request.kind === "suggestion" ? ["decline"] : request.kind === "contact" ? ["accept", "decline", "block"] : request.kind === "agent-invitation" ? ["accept", "decline"] : ["once", "always", "decline", "block"];
|
|
29262
|
+
return { ...request, actions: names.map((name) => ({ name, token: this.#actions.offer(view, name, `request:${request.id}`) })) };
|
|
29263
|
+
}
|
|
29264
|
+
taskCancel(row, role, view) {
|
|
29265
|
+
return taskCancelAction(this.#actions, { id: row.record.task.id, role, ended: isTerminal(row.record.task.status.state) }, view);
|
|
29266
|
+
}
|
|
29267
|
+
async#cancelByToken(p, token, view) {
|
|
29268
|
+
this.#joined();
|
|
29269
|
+
this.#useToken("task.cancel", token, { name: "cancel", target: `task:${p.taskId}`, view }, "redeem");
|
|
29270
|
+
const row = this.#store.task(p.taskId, "requester");
|
|
29271
|
+
if (row === undefined)
|
|
29272
|
+
throw new MessagingError("not_found", "no task this person's agent asked has that id");
|
|
29273
|
+
return this.#cancelRow(row, p, true);
|
|
29274
|
+
}
|
|
29275
|
+
async#cancelRow(row, p, owner) {
|
|
29276
|
+
const { token: _token, ...scanned } = p;
|
|
29277
|
+
this.#refuseSecrets(scanned, "task.cancel", "task cancel");
|
|
28599
29278
|
if (isTerminal(row.record.task.status.state))
|
|
28600
29279
|
throw new MessagingError("invalid_state", "the task is in a terminal state");
|
|
28601
29280
|
const env = this.#tx(() => {
|
|
@@ -28604,11 +29283,112 @@ ${row.receipt ? "receipt" : "envelope"}`;
|
|
|
28604
29283
|
return env;
|
|
28605
29284
|
});
|
|
28606
29285
|
await this.#settle([env.id]);
|
|
28607
|
-
return boundedRecord(this.#store.task(p.taskId, "requester")?.record ?? row.record, TASK_BYTES);
|
|
29286
|
+
return this.#withQuestion(await this.#viewNamed(boundedRecord(this.#store.task(p.taskId, "requester")?.record ?? row.record, TASK_BYTES)), owner);
|
|
29287
|
+
}
|
|
29288
|
+
async#nameOf(person) {
|
|
29289
|
+
const realm = this.#realm ?? this.#directory?.realm;
|
|
29290
|
+
if (!realm)
|
|
29291
|
+
return this.#named.get(person);
|
|
29292
|
+
let named;
|
|
29293
|
+
try {
|
|
29294
|
+
const profile = await realm.profile(person);
|
|
29295
|
+
named = { name: profile.name, org: profile.org };
|
|
29296
|
+
} catch (error) {
|
|
29297
|
+
if (!(error instanceof MessagingError) || error.code !== "not_found")
|
|
29298
|
+
return this.#named.get(person);
|
|
29299
|
+
named = { name: person, org: "none" };
|
|
29300
|
+
}
|
|
29301
|
+
if (this.#named.size >= NAMED_PERSONS && !this.#named.has(person))
|
|
29302
|
+
this.#named.clear();
|
|
29303
|
+
this.#named.set(person, named);
|
|
29304
|
+
return named;
|
|
29305
|
+
}
|
|
29306
|
+
#taskView(record) {
|
|
29307
|
+
const named = this.#named.get(record.peer.person);
|
|
29308
|
+
if (named === undefined)
|
|
29309
|
+
this.#nameOf(record.peer.person).catch(() => {
|
|
29310
|
+
return;
|
|
29311
|
+
});
|
|
29312
|
+
return withTaskView(record, this.#me(), named);
|
|
29313
|
+
}
|
|
29314
|
+
#setQuestion(p) {
|
|
29315
|
+
const row = this.#ownTask(p.key, p.taskId, "executor");
|
|
29316
|
+
if (p.question === null) {
|
|
29317
|
+
if (this.#questions.delete(p.taskId))
|
|
29318
|
+
this.#conversationIpc.taskChanged({ id: p.taskId, role: "executor" });
|
|
29319
|
+
return { ok: true };
|
|
29320
|
+
}
|
|
29321
|
+
if (!isInterrupted(row.record.task.status.state))
|
|
29322
|
+
throw new MessagingError("invalid_state", "only a task in auth-required or input-required waits on a question");
|
|
29323
|
+
const host = this.#store.sessionHost(p.key);
|
|
29324
|
+
if (host !== undefined && host.sessionID !== p.question.sessionId)
|
|
29325
|
+
throw new MessagingError("forbidden", "a question names the session's own host session");
|
|
29326
|
+
this.#questions.set(p.taskId, { sessionId: p.question.sessionId, approvalId: p.question.approvalId });
|
|
29327
|
+
this.#conversationIpc.taskChanged({ id: p.taskId, role: "executor" });
|
|
29328
|
+
return { ok: true };
|
|
29329
|
+
}
|
|
29330
|
+
async#personTask(row, view) {
|
|
29331
|
+
const record = this.#withQuestion(await this.#viewNamed(this.#withFollowUps(boundedRecord(row.record, TASK_BYTES))), true);
|
|
29332
|
+
const { key: _key, ...rest } = record;
|
|
29333
|
+
const cancel = this.taskCancel(row, row.record.role, view);
|
|
29334
|
+
return { ...rest, session: this.#sessionName(row), ...cancel ? { cancel } : {} };
|
|
29335
|
+
}
|
|
29336
|
+
async#personTasks(p, view) {
|
|
29337
|
+
const limit = p.limit ?? 50;
|
|
29338
|
+
const key = p.session === undefined ? undefined : this.#store.sessionByName(p.session)?.key;
|
|
29339
|
+
if (p.session !== undefined && key === undefined)
|
|
29340
|
+
return { tasks: [], total: 0 };
|
|
29341
|
+
const { rows, total, more } = this.#store.listTasks({
|
|
29342
|
+
...key !== undefined ? { key } : {},
|
|
29343
|
+
role: p.role,
|
|
29344
|
+
state: p.state,
|
|
29345
|
+
after: p.cursor === undefined ? undefined : decodeCursor(p.cursor),
|
|
29346
|
+
limit
|
|
29347
|
+
});
|
|
29348
|
+
const tasks = [];
|
|
29349
|
+
let bytes = 0;
|
|
29350
|
+
for (const row of rows) {
|
|
29351
|
+
const task = await this.#personTask(row, view);
|
|
29352
|
+
const size = jsonBytes(task);
|
|
29353
|
+
if (tasks.length > 0 && bytes + size > LIST_BYTES)
|
|
29354
|
+
break;
|
|
29355
|
+
tasks.push(task);
|
|
29356
|
+
bytes += size;
|
|
29357
|
+
}
|
|
29358
|
+
const last = tasks.at(-1);
|
|
29359
|
+
return {
|
|
29360
|
+
tasks,
|
|
29361
|
+
total,
|
|
29362
|
+
...(more || tasks.length < rows.length) && last ? { nextCursor: encodeCursor({ updatedAt: last.updatedAt, taskId: last.task.id, role: last.role }) } : {}
|
|
29363
|
+
};
|
|
29364
|
+
}
|
|
29365
|
+
async#personTaskGet(p, view) {
|
|
29366
|
+
const rows = (p.role !== undefined ? [p.role] : ["requester", "executor"]).flatMap((role) => this.#store.task(p.taskId, role) ?? []);
|
|
29367
|
+
if (rows.length === 0)
|
|
29368
|
+
throw new MessagingError("not_found", "no task of this person's agents has that id");
|
|
29369
|
+
if (rows.length > 1)
|
|
29370
|
+
throw new MessagingError("invalid_request", "this device holds both sides of that task; supply role");
|
|
29371
|
+
return await this.#personTask(rows[0], view);
|
|
29372
|
+
}
|
|
29373
|
+
async#personTaskEntry(id, role, ctx, authority) {
|
|
29374
|
+
const row = this.#store.task(id, role);
|
|
29375
|
+
return row ? await this.#personTask(row, actionView(ctx, authority, undefined)) : undefined;
|
|
29376
|
+
}
|
|
29377
|
+
#withQuestion(view, owner) {
|
|
29378
|
+
const question = this.#questions.get(view.task.id);
|
|
29379
|
+
if (!owner || question === undefined || view.role !== "executor" || !isInterrupted(view.task.status.state))
|
|
29380
|
+
return view;
|
|
29381
|
+
return { ...view, question: { ...question } };
|
|
29382
|
+
}
|
|
29383
|
+
async#viewNamed(record) {
|
|
29384
|
+
return withTaskView(record, this.#me(), await this.#nameOf(record.peer.person).catch(() => {
|
|
29385
|
+
return;
|
|
29386
|
+
}));
|
|
28608
29387
|
}
|
|
28609
29388
|
sweep() {
|
|
28610
29389
|
if (this.#closed)
|
|
28611
29390
|
return;
|
|
29391
|
+
this.#actions.sweep();
|
|
28612
29392
|
this.#directory?.sweep();
|
|
28613
29393
|
const now = this.#now();
|
|
28614
29394
|
this.#expireTickets(now);
|
|
@@ -28663,6 +29443,7 @@ ${row.receipt ? "receipt" : "envelope"}`;
|
|
|
28663
29443
|
message: agentMessage([{ text: INPUT_TIMEOUT_TEXT }], row.record.task.id, row.record.task.contextId, now),
|
|
28664
29444
|
timestamp: iso3(now)
|
|
28665
29445
|
};
|
|
29446
|
+
this.#questions.delete(row.record.task.id);
|
|
28666
29447
|
row.record.task.status = status;
|
|
28667
29448
|
row.record.updatedAt = now;
|
|
28668
29449
|
this.#store.putTask(row);
|
|
@@ -28670,26 +29451,7 @@ ${row.receipt ? "receipt" : "envelope"}`;
|
|
|
28670
29451
|
this.#sendStatus(row);
|
|
28671
29452
|
}
|
|
28672
29453
|
async#ensureCurrent(persons) {
|
|
28673
|
-
|
|
28674
|
-
const others = [...new Set(persons)].filter((p) => p !== own);
|
|
28675
|
-
if (!others.length)
|
|
28676
|
-
return;
|
|
28677
|
-
let outdated;
|
|
28678
|
-
try {
|
|
28679
|
-
outdated = (await this.#transport?.conversations?.convCaps?.(others))?.outdated ?? [];
|
|
28680
|
-
} catch {
|
|
28681
|
-
return;
|
|
28682
|
-
}
|
|
28683
|
-
if (!outdated.length)
|
|
28684
|
-
return;
|
|
28685
|
-
const handles = await Promise.all(outdated.map(async (p) => {
|
|
28686
|
-
try {
|
|
28687
|
-
return (await this.#joined().realm.profile(p)).handle;
|
|
28688
|
-
} catch {
|
|
28689
|
-
return p;
|
|
28690
|
-
}
|
|
28691
|
-
}));
|
|
28692
|
-
throw new MessagingError("peer_outdated", `${handles.join(", ")} ${handles.length === 1 ? "needs" : "need"} to update Messaging`);
|
|
29454
|
+
await ensureCurrent(this.#transport?.conversations, this.#joined().me.person, persons, async (p) => (await this.#joined().realm.profile(p)).handle);
|
|
28693
29455
|
}
|
|
28694
29456
|
conversations() {
|
|
28695
29457
|
return this.#conversationEngine();
|
|
@@ -29059,6 +29821,13 @@ ${row.receipt ? "receipt" : "envelope"}`;
|
|
|
29059
29821
|
const devices = realm.known(me.person).devices.map((d) => ({ device: d.machine, label: d.label, vouched: "admin", current: true, self: d.machine === identity.machine }));
|
|
29060
29822
|
return { devices };
|
|
29061
29823
|
}
|
|
29824
|
+
#withFeatures(d) {
|
|
29825
|
+
const raw = d.self ? [...FEATURES] : this.#store.directoryRow(d.device)?.presence.features;
|
|
29826
|
+
if (raw === undefined)
|
|
29827
|
+
return d;
|
|
29828
|
+
const features = [...new Set(raw.filter((f) => PresenceFeature.safeParse(f).success))].slice(0, 32);
|
|
29829
|
+
return { ...d, features };
|
|
29830
|
+
}
|
|
29062
29831
|
#away() {
|
|
29063
29832
|
return this.#jellyHost !== undefined && !this.#attached;
|
|
29064
29833
|
}
|
|
@@ -29094,10 +29863,37 @@ ${row.receipt ? "receipt" : "envelope"}`;
|
|
|
29094
29863
|
throw new MessagingError("invalid_request", "the wake URL is not on the realm's Jelly ID");
|
|
29095
29864
|
const agents = [...new Set(p.agents)];
|
|
29096
29865
|
const awaiting = this.#store.awaitedRequests(agents, TERMINAL_STATES, WAKE_AWAITING);
|
|
29097
|
-
const
|
|
29866
|
+
const dms = await this.#wakeDms();
|
|
29867
|
+
const sent = await home.wake({ url: p.url, ticket: p.ticket, agents, awaiting, dms });
|
|
29098
29868
|
this.#wakeArmed = true;
|
|
29099
|
-
this.#log("info", "wake.arm", { relay: sent === "sent" ? "armed" : "pending", agents: agents.length, awaiting: awaiting.length });
|
|
29100
|
-
return { relay: sent === "sent" ? "armed" : "pending", awaiting: awaiting.length };
|
|
29869
|
+
this.#log("info", "wake.arm", { relay: sent === "sent" ? "armed" : "pending", agents: agents.length, awaiting: awaiting.length, dms: dms.length });
|
|
29870
|
+
return { relay: sent === "sent" ? "armed" : "pending", awaiting: awaiting.length, dms: dms.length };
|
|
29871
|
+
}
|
|
29872
|
+
async#wakeDms() {
|
|
29873
|
+
const engine = this.#conversationEngine();
|
|
29874
|
+
if (!engine)
|
|
29875
|
+
return [];
|
|
29876
|
+
const { me, realm } = this.#joined();
|
|
29877
|
+
const orgs = new Map;
|
|
29878
|
+
const orgOf = (person) => {
|
|
29879
|
+
let org = orgs.get(person);
|
|
29880
|
+
if (!org)
|
|
29881
|
+
orgs.set(person, org = realm.profile(person).then((profile) => profile.org, () => "none"));
|
|
29882
|
+
return org;
|
|
29883
|
+
};
|
|
29884
|
+
const candidates = teammateDms(engine.list(), me.person);
|
|
29885
|
+
await realm.freshen?.(candidates.map((c) => c.person)).catch(() => {});
|
|
29886
|
+
const dms = [];
|
|
29887
|
+
for (const { cid, person } of candidates) {
|
|
29888
|
+
if (dms.length >= WAKE_DMS)
|
|
29889
|
+
break;
|
|
29890
|
+
if (this.#shouldArm(person, await orgOf(person)))
|
|
29891
|
+
dms.push(cid);
|
|
29892
|
+
}
|
|
29893
|
+
return dms;
|
|
29894
|
+
}
|
|
29895
|
+
#shouldArm(person, org) {
|
|
29896
|
+
return wakeArmable(this.#grants, person, org);
|
|
29101
29897
|
}
|
|
29102
29898
|
async#wakeDisarm() {
|
|
29103
29899
|
const home = this.#directory;
|
|
@@ -29782,6 +30578,9 @@ ${row.receipt ? "receipt" : "envelope"}`;
|
|
|
29782
30578
|
function jsonBytes(value) {
|
|
29783
30579
|
return Buffer.byteLength(JSON.stringify(value) ?? "");
|
|
29784
30580
|
}
|
|
30581
|
+
function signInMessage(message, tool) {
|
|
30582
|
+
return { ...message, metadata: { ...message.metadata, [URI.signIn]: { tool } }, extensions: [...message.extensions ?? [], URI.signIn] };
|
|
30583
|
+
}
|
|
29785
30584
|
function boundedRecord(record, limit) {
|
|
29786
30585
|
if (jsonBytes(record) <= limit)
|
|
29787
30586
|
return structuredClone(record);
|
|
@@ -29879,7 +30678,7 @@ function signInToken(token) {
|
|
|
29879
30678
|
return false;
|
|
29880
30679
|
}
|
|
29881
30680
|
}
|
|
29882
|
-
var DAY3 = 86400000, KEYCHAIN_LEFTOVERS_KEY = "keychain.stale", DEFAULT_TUNING, KEPT_MS, INPUT_TIMEOUT_MS, TASK_BYTES = 1048576, PREDECESSOR_KEY = "identity.predecessor", REPLACE_LABEL_KEY = "identity.replace.label", REPLACED_KEY = "identity.replaced", ERASURES_PER_DAY = 10, DEVICE_NOTICES_KEY = "notice.devices.seq", TRUST_NOTICE_MS, DEVICES_CHANGED_MS, MACHINE_KEY = "identity.machine", DEFAULT_INVITE_TTL_MS, LIST_BYTES = 1572864;
|
|
30681
|
+
var DAY3 = 86400000, KEYCHAIN_LEFTOVERS_KEY = "keychain.stale", DEFAULT_TUNING, KEPT_MS, INPUT_TIMEOUT_MS, TASK_BYTES = 1048576, PREDECESSOR_KEY = "identity.predecessor", REPLACE_LABEL_KEY = "identity.replace.label", REPLACED_KEY = "identity.replaced", ERASURES_PER_DAY = 10, DEVICE_NOTICES_KEY = "notice.devices.seq", TRUST_NOTICE_MS, DEVICES_CHANGED_MS, MACHINE_KEY = "identity.machine", DEFAULT_INVITE_TTL_MS, LIST_BYTES = 1572864, NAMED_PERSONS = 1000;
|
|
29883
30682
|
var init_daemon = __esm(() => {
|
|
29884
30683
|
init_dist();
|
|
29885
30684
|
init_dist2();
|
|
@@ -29908,6 +30707,7 @@ var init_daemon = __esm(() => {
|
|
|
29908
30707
|
init_answer_preview();
|
|
29909
30708
|
init_snapshot();
|
|
29910
30709
|
init_notices();
|
|
30710
|
+
init_task_view();
|
|
29911
30711
|
init_resolve();
|
|
29912
30712
|
init_secrets();
|
|
29913
30713
|
init_roster_sync();
|
|
@@ -29916,10 +30716,13 @@ var init_daemon = __esm(() => {
|
|
|
29916
30716
|
init_tasks2();
|
|
29917
30717
|
init_version();
|
|
29918
30718
|
init_wake_bridge();
|
|
30719
|
+
init_action_tokens();
|
|
29919
30720
|
init_grants();
|
|
29920
30721
|
init_holds();
|
|
29921
30722
|
init_cards2();
|
|
29922
30723
|
init_requests2();
|
|
30724
|
+
init_reach();
|
|
30725
|
+
init_conv_caps();
|
|
29923
30726
|
init_sentence();
|
|
29924
30727
|
DEFAULT_TUNING = {
|
|
29925
30728
|
presenceMinMs: 2000,
|
|
@@ -30035,6 +30838,15 @@ CREATE TABLE IF NOT EXISTS conv_request_decisions (
|
|
|
30035
30838
|
decision TEXT NOT NULL,
|
|
30036
30839
|
PRIMARY KEY (realm, id)
|
|
30037
30840
|
);
|
|
30841
|
+
`,
|
|
30842
|
+
`
|
|
30843
|
+
CREATE TABLE IF NOT EXISTS dir_wake_dms (
|
|
30844
|
+
device TEXT NOT NULL,
|
|
30845
|
+
cid TEXT NOT NULL,
|
|
30846
|
+
exp INTEGER NOT NULL,
|
|
30847
|
+
PRIMARY KEY (device, cid)
|
|
30848
|
+
);
|
|
30849
|
+
CREATE INDEX IF NOT EXISTS dir_wake_dms_exp ON dir_wake_dms (exp);
|
|
30038
30850
|
`
|
|
30039
30851
|
];
|
|
30040
30852
|
RELAY_SCHEMA = 1 + MIGRATIONS2.length;
|
|
@@ -30350,7 +31162,7 @@ class RelayClient {
|
|
|
30350
31162
|
return this.#dir("dir.ok", { t: "presence.watch", persons });
|
|
30351
31163
|
}
|
|
30352
31164
|
wakeArm(arm) {
|
|
30353
|
-
return this.#dir("dir.ok", { t: "wake.arm", url: arm.url, ticket: arm.ticket, agents: arm.agents, awaiting: arm.awaiting });
|
|
31165
|
+
return this.#dir("dir.ok", { t: "wake.arm", url: arm.url, ticket: arm.ticket, agents: arm.agents, awaiting: arm.awaiting, ...arm.dms ? { dms: arm.dms } : {} });
|
|
30354
31166
|
}
|
|
30355
31167
|
wakeDisarm() {
|
|
30356
31168
|
return this.#dir("dir.ok", { t: "wake.disarm" });
|
|
@@ -31308,6 +32120,7 @@ var HINTS = {
|
|
|
31308
32120
|
login_denied: "the sign-in was denied at Jelly ID; run messaging login again if it was you, or check the account.",
|
|
31309
32121
|
log_conflict: "retry: the device log changed at the same time.",
|
|
31310
32122
|
conv_conflict: "retry: the conversation changed at the same time.",
|
|
32123
|
+
conflict: "your settings changed at the same time: run the command again to see them, then choose again.",
|
|
31311
32124
|
stale_epoch: "retry: the conversation's keys changed at the same time.",
|
|
31312
32125
|
device_limit: "remove one of this person's devices first.",
|
|
31313
32126
|
admission_required: "bind this device from Jelly, or approve it from one of your Jelly devices.",
|
|
@@ -31951,6 +32764,72 @@ var grant2 = {
|
|
|
31951
32764
|
function optional2(key, value) {
|
|
31952
32765
|
return value === undefined ? {} : { [key]: value };
|
|
31953
32766
|
}
|
|
32767
|
+
function choiceLabel(group, value) {
|
|
32768
|
+
return group.options.find((o) => o.value === value)?.label ?? value;
|
|
32769
|
+
}
|
|
32770
|
+
function printReach(ctx, snapshot) {
|
|
32771
|
+
ctx.out.lines(table(["QUESTION", "CHOICE", "MEANS"], snapshot.groups.map((g) => {
|
|
32772
|
+
const option = g.options.find((o) => o.value === (g.value ?? "ask"));
|
|
32773
|
+
return [g.label, g.value === null ? `${choiceLabel(g, "ask")} (the default)` : choiceLabel(g, g.value), option?.description ?? ""];
|
|
32774
|
+
}), ctx.out.style));
|
|
32775
|
+
for (const g of snapshot.groups) {
|
|
32776
|
+
if (g.lapsed !== undefined)
|
|
32777
|
+
ctx.out.line(ctx.out.style.yellow(`${g.label}: ${choiceLabel(g, g.lapsed)} lapsed with the device that chose it; choose again.`));
|
|
32778
|
+
}
|
|
32779
|
+
}
|
|
32780
|
+
var reach = {
|
|
32781
|
+
name: "reach",
|
|
32782
|
+
summary: "Show or set who can message you and how agents may ask yours: open, ask (me first) or never",
|
|
32783
|
+
usage: "[--message open|ask|never] [--ask open|ask|never] [--dry-run]",
|
|
32784
|
+
details: [
|
|
32785
|
+
"--message (Who can message you) is for people outside your org: open lets anyone message you; ask (the default)",
|
|
32786
|
+
"has them send a message request first; never refuses them and their requests. Your org can always message you.",
|
|
32787
|
+
"--ask (How agents may ask yours) is for your teammates' agents: open lets them ask your agents without asking you",
|
|
32788
|
+
"(guarded); ask (the default) holds each request for you (messaging requests); never refuses them. Agents of people",
|
|
32789
|
+
"outside your org never may. A grant you set for one person (messaging grant) wins over either choice.",
|
|
32790
|
+
"With no option it shows your choices. --dry-run prints what the change would do and changes nothing.",
|
|
32791
|
+
"A device of yours on an older Messaging release does not enforce these choices: update every device."
|
|
32792
|
+
].join(`
|
|
32793
|
+
`),
|
|
32794
|
+
options: {
|
|
32795
|
+
message: { type: "string", value: "open|ask|never", description: "Who can message you (people outside your org)" },
|
|
32796
|
+
ask: { type: "string", value: "open|ask|never", description: "How your teammates' agents may ask yours" },
|
|
32797
|
+
"dry-run": { type: "boolean", description: "Print what it would do and change nothing" }
|
|
32798
|
+
},
|
|
32799
|
+
args: { min: 0, max: 0 },
|
|
32800
|
+
async run(ctx, _positionals, values) {
|
|
32801
|
+
const changes = {
|
|
32802
|
+
...optional2("message", levelOption(values, "message", ReachValue, "open, ask or never")),
|
|
32803
|
+
...optional2("ask", levelOption(values, "ask", ReachValue, "open, ask or never"))
|
|
32804
|
+
};
|
|
32805
|
+
const none = Object.keys(changes).length === 0;
|
|
32806
|
+
if (none && values["dry-run"] === true)
|
|
32807
|
+
throw new UsageError("--dry-run goes with --message or --ask");
|
|
32808
|
+
const client = await ctx.client();
|
|
32809
|
+
const shown = await client.call("reach.get", {});
|
|
32810
|
+
if (none) {
|
|
32811
|
+
if (ctx.json)
|
|
32812
|
+
return ctx.out.json(shown);
|
|
32813
|
+
printReach(ctx, shown);
|
|
32814
|
+
ctx.out.note("Change with: messaging reach --message open|ask|never --ask open|ask|never");
|
|
32815
|
+
return;
|
|
32816
|
+
}
|
|
32817
|
+
const params = { revision: shown.revision, changes };
|
|
32818
|
+
const { sentence } = await client.call("reach.set.preview", params);
|
|
32819
|
+
if (values["dry-run"] === true) {
|
|
32820
|
+
if (ctx.json)
|
|
32821
|
+
ctx.out.json({ sentence });
|
|
32822
|
+
else
|
|
32823
|
+
ctx.out.line(sentence);
|
|
32824
|
+
return;
|
|
32825
|
+
}
|
|
32826
|
+
const result = await client.call("reach.set", params);
|
|
32827
|
+
if (ctx.json)
|
|
32828
|
+
return ctx.out.json({ sentence, ...result });
|
|
32829
|
+
ctx.out.line(sentence);
|
|
32830
|
+
printReach(ctx, result);
|
|
32831
|
+
}
|
|
32832
|
+
};
|
|
31954
32833
|
var requests2 = {
|
|
31955
32834
|
name: "requests",
|
|
31956
32835
|
summary: "List the requests waiting for you, or answer one: once, always, accept, decline or block",
|
|
@@ -31986,7 +32865,7 @@ var requests2 = {
|
|
|
31986
32865
|
throw new UsageError("--all goes with `requests answer`");
|
|
31987
32866
|
const { requests: held } = await client.call("request.list", {});
|
|
31988
32867
|
if (ctx.json) {
|
|
31989
|
-
ctx.out.json({ requests: held });
|
|
32868
|
+
ctx.out.json({ requests: held.map(({ actions: _actions, ...request }) => request) });
|
|
31990
32869
|
return;
|
|
31991
32870
|
}
|
|
31992
32871
|
if (held.length === 0) {
|
|
@@ -32072,7 +32951,7 @@ var card = {
|
|
|
32072
32951
|
ctx.out.line(sent ? "Asked. Their card comes when one of their devices answers." : "You asked them in the last day; their card comes when they answer.");
|
|
32073
32952
|
}
|
|
32074
32953
|
};
|
|
32075
|
-
var CONSENT_COMMANDS = [grants, grant2, requests2, contact, card];
|
|
32954
|
+
var CONSENT_COMMANDS = [grants, grant2, reach, requests2, contact, card];
|
|
32076
32955
|
|
|
32077
32956
|
// packages/daemon/dist/cli/commands/daemon.js
|
|
32078
32957
|
init_dist();
|
|
@@ -32941,6 +33820,8 @@ function conversationEvent(event) {
|
|
|
32941
33820
|
return { type: event.type };
|
|
32942
33821
|
case "gap":
|
|
32943
33822
|
return { type: event.type, pos: event.pos };
|
|
33823
|
+
case "task":
|
|
33824
|
+
return { type: event.type, task: event.task.task.id, role: event.task.role, state: event.task.task.status.state };
|
|
32944
33825
|
}
|
|
32945
33826
|
}
|
|
32946
33827
|
async function syncConversations(client, ctx, position) {
|
|
@@ -34031,7 +34912,7 @@ var SECTIONS = [
|
|
|
34031
34912
|
["Daemon", ["daemon start", "daemon stop", "status"]],
|
|
34032
34913
|
["Team", ["team prepare", "team create", "team card", "team invite", "team join", "team pair", "team add", "team claim", "team pair-cancel", "team members", "team remove", "team role", "team revoke-invite"]],
|
|
34033
34914
|
["Identity", ["identity show", "identity migrate", "identity rotate-enc", "identity replace", "erase"]],
|
|
34034
|
-
["People", ["login", "people", "devices", "grants", "grant", "requests", "contact", "card", "notices"]],
|
|
34915
|
+
["People", ["login", "people", "devices", "grants", "grant", "reach", "requests", "contact", "card", "notices"]],
|
|
34035
34916
|
["Messages", ["who", "send", "inbox", "read"]],
|
|
34036
34917
|
["Chats", ["chat list", "chat read", "chat post", "chat search"]],
|
|
34037
34918
|
["Tasks", ["tasks", "task", "cancel"]],
|