switchroom 0.12.14 → 0.12.15
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/dist/cli/switchroom.js +367 -278
- package/dist/vault/approvals/kernel-server.js +68 -1
- package/dist/vault/broker/server.js +21 -1
- package/package.json +1 -1
- package/telegram-plugin/dist/gateway/gateway.js +96 -70
- package/telegram-plugin/gateway/approval-callback.test.ts +49 -1
- package/telegram-plugin/gateway/approval-callback.ts +85 -67
- package/telegram-plugin/gateway/gateway.ts +19 -2
- package/telegram-plugin/gateway/pending-inbound-buffer.ts +39 -0
- package/telegram-plugin/tests/pending-inbound-buffer.test.ts +71 -1
|
@@ -11788,6 +11788,15 @@ var ApprovalRecordRequestSchema = exports_external.object({
|
|
|
11788
11788
|
granted_by_user_id: exports_external.number().int(),
|
|
11789
11789
|
ttl_ms: exports_external.number().int().positive().nullable().optional()
|
|
11790
11790
|
});
|
|
11791
|
+
var ApprovalConsumeRecordRequestSchema = exports_external.object({
|
|
11792
|
+
v: exports_external.literal(1),
|
|
11793
|
+
op: exports_external.literal("approval_consume_record"),
|
|
11794
|
+
request_id: exports_external.string().regex(/^[0-9a-f]{32}$/),
|
|
11795
|
+
decision: ApprovalDecisionModeSchema,
|
|
11796
|
+
approver_set: exports_external.array(exports_external.string()),
|
|
11797
|
+
granted_by_user_id: exports_external.number().int(),
|
|
11798
|
+
ttl_ms: exports_external.number().int().positive().nullable().optional()
|
|
11799
|
+
});
|
|
11791
11800
|
var RequestSchema = exports_external.discriminatedUnion("op", [
|
|
11792
11801
|
GetRequestSchema,
|
|
11793
11802
|
PutRequestSchema,
|
|
@@ -11803,7 +11812,8 @@ var RequestSchema = exports_external.discriminatedUnion("op", [
|
|
|
11803
11812
|
ApprovalConsumeRequestSchema,
|
|
11804
11813
|
ApprovalRevokeRequestSchema,
|
|
11805
11814
|
ApprovalListRequestSchema,
|
|
11806
|
-
ApprovalRecordRequestSchema
|
|
11815
|
+
ApprovalRecordRequestSchema,
|
|
11816
|
+
ApprovalConsumeRecordRequestSchema
|
|
11807
11817
|
]);
|
|
11808
11818
|
var VaultEntrySchema = exports_external.union([
|
|
11809
11819
|
exports_external.object({ kind: exports_external.literal("string"), value: exports_external.string() }),
|
|
@@ -11925,6 +11935,15 @@ var OkApprovalRecordResponseSchema = exports_external.object({
|
|
|
11925
11935
|
ok: exports_external.literal(true),
|
|
11926
11936
|
decision_id: exports_external.string()
|
|
11927
11937
|
});
|
|
11938
|
+
var OkApprovalConsumeRecordResponseSchema = exports_external.object({
|
|
11939
|
+
ok: exports_external.literal(true),
|
|
11940
|
+
consumed: exports_external.boolean(),
|
|
11941
|
+
decision_id: exports_external.string().optional(),
|
|
11942
|
+
agent_unit: exports_external.string().optional(),
|
|
11943
|
+
scope: exports_external.string().optional(),
|
|
11944
|
+
action: exports_external.string().optional(),
|
|
11945
|
+
why: exports_external.string().nullable().optional()
|
|
11946
|
+
});
|
|
11928
11947
|
var ErrorResponseSchema = exports_external.object({
|
|
11929
11948
|
ok: exports_external.literal(false),
|
|
11930
11949
|
code: ErrorCode,
|
|
@@ -11946,6 +11965,7 @@ var ResponseSchema = exports_external.union([
|
|
|
11946
11965
|
OkApprovalRevokeResponseSchema,
|
|
11947
11966
|
OkApprovalListResponseSchema,
|
|
11948
11967
|
OkApprovalRecordResponseSchema,
|
|
11968
|
+
OkApprovalConsumeRecordResponseSchema,
|
|
11949
11969
|
ErrorResponseSchema
|
|
11950
11970
|
]);
|
|
11951
11971
|
function decodeRequest(line) {
|
|
@@ -12264,6 +12284,22 @@ function recordDecision(db, input, now = Date.now()) {
|
|
|
12264
12284
|
});
|
|
12265
12285
|
return id;
|
|
12266
12286
|
}
|
|
12287
|
+
function consumeAndRecord(db, input, now = Date.now()) {
|
|
12288
|
+
const run = db.transaction(() => {
|
|
12289
|
+
const nonce = consumeNonce(db, input.request_id, now);
|
|
12290
|
+
if (nonce === null)
|
|
12291
|
+
return { consumed: false };
|
|
12292
|
+
const decision_id = recordDecision(db, {
|
|
12293
|
+
nonce,
|
|
12294
|
+
decision: input.decision,
|
|
12295
|
+
approver_set: input.approver_set,
|
|
12296
|
+
granted_by_user_id: input.granted_by_user_id,
|
|
12297
|
+
ttl_ms: input.ttl_ms
|
|
12298
|
+
}, now);
|
|
12299
|
+
return { consumed: true, decision_id, nonce };
|
|
12300
|
+
});
|
|
12301
|
+
return run();
|
|
12302
|
+
}
|
|
12267
12303
|
function revokeDecision(db, decision_id, actor, reason, now = Date.now()) {
|
|
12268
12304
|
const row = db.query(`SELECT * FROM approval_decisions WHERE id = ?`).get(decision_id);
|
|
12269
12305
|
if (!row)
|
|
@@ -12973,6 +13009,37 @@ function handleRequest(socket, req, agent, db, peerUid, isOperator = false) {
|
|
|
12973
13009
|
socket.write(encodeResponse({ ok: true, decision_id }));
|
|
12974
13010
|
return;
|
|
12975
13011
|
}
|
|
13012
|
+
if (req.op === "approval_consume_record") {
|
|
13013
|
+
const peek = getNonce(db, req.request_id);
|
|
13014
|
+
if (peek !== null) {
|
|
13015
|
+
const acl = checkApprovalAclByAgent(agent, peek.agent_unit);
|
|
13016
|
+
if (!acl.allow) {
|
|
13017
|
+
socket.write(encodeResponse(errorResponse("DENIED", "approval_consume_record denied: nonce does not belong to the calling agent")));
|
|
13018
|
+
return;
|
|
13019
|
+
}
|
|
13020
|
+
}
|
|
13021
|
+
const res = consumeAndRecord(db, {
|
|
13022
|
+
request_id: req.request_id,
|
|
13023
|
+
decision: req.decision,
|
|
13024
|
+
approver_set: req.approver_set,
|
|
13025
|
+
granted_by_user_id: req.granted_by_user_id,
|
|
13026
|
+
ttl_ms: req.ttl_ms ?? undefined
|
|
13027
|
+
});
|
|
13028
|
+
if (!res.consumed) {
|
|
13029
|
+
socket.write(encodeResponse({ ok: true, consumed: false }));
|
|
13030
|
+
return;
|
|
13031
|
+
}
|
|
13032
|
+
socket.write(encodeResponse({
|
|
13033
|
+
ok: true,
|
|
13034
|
+
consumed: true,
|
|
13035
|
+
decision_id: res.decision_id,
|
|
13036
|
+
agent_unit: res.nonce.agent_unit,
|
|
13037
|
+
scope: res.nonce.scope,
|
|
13038
|
+
action: res.nonce.action,
|
|
13039
|
+
why: res.nonce.why
|
|
13040
|
+
}));
|
|
13041
|
+
return;
|
|
13042
|
+
}
|
|
12976
13043
|
if (req.op === "approval_list") {
|
|
12977
13044
|
const decisions = listDecisions(db, { agent_unit: req.agent_unit });
|
|
12978
13045
|
const meta = decisions.map((d) => ({
|
|
@@ -13216,6 +13216,15 @@ var ApprovalRecordRequestSchema = exports_external.object({
|
|
|
13216
13216
|
granted_by_user_id: exports_external.number().int(),
|
|
13217
13217
|
ttl_ms: exports_external.number().int().positive().nullable().optional()
|
|
13218
13218
|
});
|
|
13219
|
+
var ApprovalConsumeRecordRequestSchema = exports_external.object({
|
|
13220
|
+
v: exports_external.literal(1),
|
|
13221
|
+
op: exports_external.literal("approval_consume_record"),
|
|
13222
|
+
request_id: exports_external.string().regex(/^[0-9a-f]{32}$/),
|
|
13223
|
+
decision: ApprovalDecisionModeSchema,
|
|
13224
|
+
approver_set: exports_external.array(exports_external.string()),
|
|
13225
|
+
granted_by_user_id: exports_external.number().int(),
|
|
13226
|
+
ttl_ms: exports_external.number().int().positive().nullable().optional()
|
|
13227
|
+
});
|
|
13219
13228
|
var RequestSchema = exports_external.discriminatedUnion("op", [
|
|
13220
13229
|
GetRequestSchema,
|
|
13221
13230
|
PutRequestSchema,
|
|
@@ -13231,7 +13240,8 @@ var RequestSchema = exports_external.discriminatedUnion("op", [
|
|
|
13231
13240
|
ApprovalConsumeRequestSchema,
|
|
13232
13241
|
ApprovalRevokeRequestSchema,
|
|
13233
13242
|
ApprovalListRequestSchema,
|
|
13234
|
-
ApprovalRecordRequestSchema
|
|
13243
|
+
ApprovalRecordRequestSchema,
|
|
13244
|
+
ApprovalConsumeRecordRequestSchema
|
|
13235
13245
|
]);
|
|
13236
13246
|
var VaultEntrySchema = exports_external.union([
|
|
13237
13247
|
exports_external.object({ kind: exports_external.literal("string"), value: exports_external.string() }),
|
|
@@ -13353,6 +13363,15 @@ var OkApprovalRecordResponseSchema = exports_external.object({
|
|
|
13353
13363
|
ok: exports_external.literal(true),
|
|
13354
13364
|
decision_id: exports_external.string()
|
|
13355
13365
|
});
|
|
13366
|
+
var OkApprovalConsumeRecordResponseSchema = exports_external.object({
|
|
13367
|
+
ok: exports_external.literal(true),
|
|
13368
|
+
consumed: exports_external.boolean(),
|
|
13369
|
+
decision_id: exports_external.string().optional(),
|
|
13370
|
+
agent_unit: exports_external.string().optional(),
|
|
13371
|
+
scope: exports_external.string().optional(),
|
|
13372
|
+
action: exports_external.string().optional(),
|
|
13373
|
+
why: exports_external.string().nullable().optional()
|
|
13374
|
+
});
|
|
13356
13375
|
var ErrorResponseSchema = exports_external.object({
|
|
13357
13376
|
ok: exports_external.literal(false),
|
|
13358
13377
|
code: ErrorCode,
|
|
@@ -13374,6 +13393,7 @@ var ResponseSchema = exports_external.union([
|
|
|
13374
13393
|
OkApprovalRevokeResponseSchema,
|
|
13375
13394
|
OkApprovalListResponseSchema,
|
|
13376
13395
|
OkApprovalRecordResponseSchema,
|
|
13396
|
+
OkApprovalConsumeRecordResponseSchema,
|
|
13377
13397
|
ErrorResponseSchema
|
|
13378
13398
|
]);
|
|
13379
13399
|
function decodeRequest(line) {
|
package/package.json
CHANGED
|
@@ -24664,7 +24664,7 @@ function decodeResponse2(line) {
|
|
|
24664
24664
|
const obj = JSON.parse(line);
|
|
24665
24665
|
return ResponseSchema2.parse(obj);
|
|
24666
24666
|
}
|
|
24667
|
-
var MAX_FRAME_BYTES2, GetRequestSchema, PutRequestSchema, ListRequestSchema, MintGrantRequestSchema, ListGrantsRequestSchema, RevokeGrantRequestSchema, StatusRequestSchema, LockRequestSchema, PreflightAccessRequestSchema, OkPreflightAccessResponseSchema, ApprovalRequestRequestSchema, ApprovalLookupRequestSchema, ApprovalConsumeRequestSchema, ApprovalRevokeRequestSchema, ApprovalListRequestSchema, ApprovalDecisionModeSchema, ApprovalRecordRequestSchema, RequestSchema2, VaultEntrySchema, ErrorCode, OkEntryResponseSchema, OkKeysResponseSchema, BrokerStatus, OkStatusResponseSchema, OkLockResponseSchema, OkPutResponseSchema, OkMintGrantResponseSchema, GrantMetaSchema, OkListGrantsResponseSchema, OkRevokeGrantResponseSchema, OkApprovalRequestResponseSchema, ApprovalDecisionMetaSchema, OkApprovalLookupResponseSchema, OkApprovalConsumeResponseSchema, OkApprovalRevokeResponseSchema, OkApprovalListResponseSchema, OkApprovalRecordResponseSchema, ErrorResponseSchema2, ResponseSchema2;
|
|
24667
|
+
var MAX_FRAME_BYTES2, GetRequestSchema, PutRequestSchema, ListRequestSchema, MintGrantRequestSchema, ListGrantsRequestSchema, RevokeGrantRequestSchema, StatusRequestSchema, LockRequestSchema, PreflightAccessRequestSchema, OkPreflightAccessResponseSchema, ApprovalRequestRequestSchema, ApprovalLookupRequestSchema, ApprovalConsumeRequestSchema, ApprovalRevokeRequestSchema, ApprovalListRequestSchema, ApprovalDecisionModeSchema, ApprovalRecordRequestSchema, ApprovalConsumeRecordRequestSchema, RequestSchema2, VaultEntrySchema, ErrorCode, OkEntryResponseSchema, OkKeysResponseSchema, BrokerStatus, OkStatusResponseSchema, OkLockResponseSchema, OkPutResponseSchema, OkMintGrantResponseSchema, GrantMetaSchema, OkListGrantsResponseSchema, OkRevokeGrantResponseSchema, OkApprovalRequestResponseSchema, ApprovalDecisionMetaSchema, OkApprovalLookupResponseSchema, OkApprovalConsumeResponseSchema, OkApprovalRevokeResponseSchema, OkApprovalListResponseSchema, OkApprovalRecordResponseSchema, OkApprovalConsumeRecordResponseSchema, ErrorResponseSchema2, ResponseSchema2;
|
|
24668
24668
|
var init_protocol2 = __esm(() => {
|
|
24669
24669
|
init_zod();
|
|
24670
24670
|
MAX_FRAME_BYTES2 = 64 * 1024;
|
|
@@ -24792,6 +24792,15 @@ var init_protocol2 = __esm(() => {
|
|
|
24792
24792
|
granted_by_user_id: exports_external.number().int(),
|
|
24793
24793
|
ttl_ms: exports_external.number().int().positive().nullable().optional()
|
|
24794
24794
|
});
|
|
24795
|
+
ApprovalConsumeRecordRequestSchema = exports_external.object({
|
|
24796
|
+
v: exports_external.literal(1),
|
|
24797
|
+
op: exports_external.literal("approval_consume_record"),
|
|
24798
|
+
request_id: exports_external.string().regex(/^[0-9a-f]{32}$/),
|
|
24799
|
+
decision: ApprovalDecisionModeSchema,
|
|
24800
|
+
approver_set: exports_external.array(exports_external.string()),
|
|
24801
|
+
granted_by_user_id: exports_external.number().int(),
|
|
24802
|
+
ttl_ms: exports_external.number().int().positive().nullable().optional()
|
|
24803
|
+
});
|
|
24795
24804
|
RequestSchema2 = exports_external.discriminatedUnion("op", [
|
|
24796
24805
|
GetRequestSchema,
|
|
24797
24806
|
PutRequestSchema,
|
|
@@ -24807,7 +24816,8 @@ var init_protocol2 = __esm(() => {
|
|
|
24807
24816
|
ApprovalConsumeRequestSchema,
|
|
24808
24817
|
ApprovalRevokeRequestSchema,
|
|
24809
24818
|
ApprovalListRequestSchema,
|
|
24810
|
-
ApprovalRecordRequestSchema
|
|
24819
|
+
ApprovalRecordRequestSchema,
|
|
24820
|
+
ApprovalConsumeRecordRequestSchema
|
|
24811
24821
|
]);
|
|
24812
24822
|
VaultEntrySchema = exports_external.union([
|
|
24813
24823
|
exports_external.object({ kind: exports_external.literal("string"), value: exports_external.string() }),
|
|
@@ -24929,6 +24939,15 @@ var init_protocol2 = __esm(() => {
|
|
|
24929
24939
|
ok: exports_external.literal(true),
|
|
24930
24940
|
decision_id: exports_external.string()
|
|
24931
24941
|
});
|
|
24942
|
+
OkApprovalConsumeRecordResponseSchema = exports_external.object({
|
|
24943
|
+
ok: exports_external.literal(true),
|
|
24944
|
+
consumed: exports_external.boolean(),
|
|
24945
|
+
decision_id: exports_external.string().optional(),
|
|
24946
|
+
agent_unit: exports_external.string().optional(),
|
|
24947
|
+
scope: exports_external.string().optional(),
|
|
24948
|
+
action: exports_external.string().optional(),
|
|
24949
|
+
why: exports_external.string().nullable().optional()
|
|
24950
|
+
});
|
|
24932
24951
|
ErrorResponseSchema2 = exports_external.object({
|
|
24933
24952
|
ok: exports_external.literal(false),
|
|
24934
24953
|
code: ErrorCode,
|
|
@@ -24950,6 +24969,7 @@ var init_protocol2 = __esm(() => {
|
|
|
24950
24969
|
OkApprovalRevokeResponseSchema,
|
|
24951
24970
|
OkApprovalListResponseSchema,
|
|
24952
24971
|
OkApprovalRecordResponseSchema,
|
|
24972
|
+
OkApprovalConsumeRecordResponseSchema,
|
|
24953
24973
|
ErrorResponseSchema2
|
|
24954
24974
|
]);
|
|
24955
24975
|
});
|
|
@@ -29132,20 +29152,6 @@ function withKernelOpts2(opts) {
|
|
|
29132
29152
|
return opts;
|
|
29133
29153
|
return { ...opts ?? {}, socket: sock };
|
|
29134
29154
|
}
|
|
29135
|
-
async function approvalConsume2(request_id, opts) {
|
|
29136
|
-
const r = await rpcRaw({ v: 1, op: "approval_consume", request_id }, withKernelOpts2(opts));
|
|
29137
|
-
if (r.kind !== "response" || !r.resp.ok)
|
|
29138
|
-
return null;
|
|
29139
|
-
if (!("consumed" in r.resp))
|
|
29140
|
-
return null;
|
|
29141
|
-
return {
|
|
29142
|
-
consumed: r.resp.consumed,
|
|
29143
|
-
agent_unit: r.resp.agent_unit,
|
|
29144
|
-
scope: r.resp.scope,
|
|
29145
|
-
action: r.resp.action,
|
|
29146
|
-
why: r.resp.why ?? null
|
|
29147
|
-
};
|
|
29148
|
-
}
|
|
29149
29155
|
async function approvalRevoke(decision_id, actor, reason, opts) {
|
|
29150
29156
|
const r = await rpcRaw({ v: 1, op: "approval_revoke", decision_id, actor, reason }, withKernelOpts2(opts));
|
|
29151
29157
|
if (r.kind !== "response" || !r.resp.ok)
|
|
@@ -29154,10 +29160,10 @@ async function approvalRevoke(decision_id, actor, reason, opts) {
|
|
|
29154
29160
|
return null;
|
|
29155
29161
|
return r.resp.revoked;
|
|
29156
29162
|
}
|
|
29157
|
-
async function
|
|
29163
|
+
async function approvalConsumeRecord(args, opts) {
|
|
29158
29164
|
const r = await rpcRaw({
|
|
29159
29165
|
v: 1,
|
|
29160
|
-
op: "
|
|
29166
|
+
op: "approval_consume_record",
|
|
29161
29167
|
request_id: args.request_id,
|
|
29162
29168
|
decision: args.decision,
|
|
29163
29169
|
approver_set: args.approver_set,
|
|
@@ -29166,9 +29172,17 @@ async function approvalRecord2(args, opts) {
|
|
|
29166
29172
|
}, withKernelOpts2(opts));
|
|
29167
29173
|
if (r.kind !== "response" || !r.resp.ok)
|
|
29168
29174
|
return null;
|
|
29169
|
-
if (!("
|
|
29175
|
+
if (!("consumed" in r.resp))
|
|
29170
29176
|
return null;
|
|
29171
|
-
|
|
29177
|
+
const resp = r.resp;
|
|
29178
|
+
return {
|
|
29179
|
+
consumed: resp.consumed,
|
|
29180
|
+
decision_id: resp.decision_id,
|
|
29181
|
+
agent_unit: resp.agent_unit,
|
|
29182
|
+
scope: resp.scope,
|
|
29183
|
+
action: resp.action,
|
|
29184
|
+
why: resp.why ?? null
|
|
29185
|
+
};
|
|
29172
29186
|
}
|
|
29173
29187
|
async function approvalList(agent_unit, opts) {
|
|
29174
29188
|
const r = await rpcRaw({ v: 1, op: "approval_list", agent_unit }, withKernelOpts2(opts));
|
|
@@ -29615,9 +29629,26 @@ var init_approval_card = __esm(() => {
|
|
|
29615
29629
|
// gateway/approval-callback.ts
|
|
29616
29630
|
var exports_approval_callback = {};
|
|
29617
29631
|
__export(exports_approval_callback, {
|
|
29632
|
+
resolveApprovalDecision: () => resolveApprovalDecision,
|
|
29618
29633
|
handleApprovalCallback: () => handleApprovalCallback,
|
|
29619
29634
|
buildGrantedKeyboard: () => buildGrantedKeyboard
|
|
29620
29635
|
});
|
|
29636
|
+
function resolveApprovalDecision(choice) {
|
|
29637
|
+
switch (choice.kind) {
|
|
29638
|
+
case "deny":
|
|
29639
|
+
return { ok: true, decision: "deny", granted: false, ttl_ms: null, displayMode: "denied" };
|
|
29640
|
+
case "once":
|
|
29641
|
+
return { ok: true, decision: "allow_once", granted: true, ttl_ms: null, displayMode: "granted once" };
|
|
29642
|
+
case "always":
|
|
29643
|
+
return { ok: true, decision: "allow_always", granted: true, ttl_ms: null, displayMode: "granted always" };
|
|
29644
|
+
case "ttl": {
|
|
29645
|
+
const ms = ttlMsFromToken(choice.param);
|
|
29646
|
+
if (ms === null)
|
|
29647
|
+
return { ok: false, error: "bad ttl token" };
|
|
29648
|
+
return { ok: true, decision: "allow_ttl", granted: true, ttl_ms: ms, displayMode: `granted for ${choice.param}` };
|
|
29649
|
+
}
|
|
29650
|
+
}
|
|
29651
|
+
}
|
|
29621
29652
|
function buildGrantedKeyboard(scope) {
|
|
29622
29653
|
const btn = scopeToOpenInDriveButton(scope);
|
|
29623
29654
|
if (btn === null)
|
|
@@ -29630,64 +29661,37 @@ async function handleApprovalCallback(ctx, data) {
|
|
|
29630
29661
|
await ctx.answerCallbackQuery({ text: "malformed approval callback" });
|
|
29631
29662
|
return;
|
|
29632
29663
|
}
|
|
29633
|
-
|
|
29634
|
-
|
|
29635
|
-
|
|
29636
|
-
let displayMode;
|
|
29637
|
-
switch (parsed.choice.kind) {
|
|
29638
|
-
case "deny":
|
|
29639
|
-
decision = "deny";
|
|
29640
|
-
granted = false;
|
|
29641
|
-
displayMode = "denied";
|
|
29642
|
-
break;
|
|
29643
|
-
case "once":
|
|
29644
|
-
decision = "allow_once";
|
|
29645
|
-
granted = true;
|
|
29646
|
-
displayMode = "granted once";
|
|
29647
|
-
break;
|
|
29648
|
-
case "always":
|
|
29649
|
-
decision = "allow_always";
|
|
29650
|
-
granted = true;
|
|
29651
|
-
displayMode = "granted always";
|
|
29652
|
-
break;
|
|
29653
|
-
case "ttl": {
|
|
29654
|
-
decision = "allow_ttl";
|
|
29655
|
-
granted = true;
|
|
29656
|
-
const ms = ttlMsFromToken(parsed.choice.param);
|
|
29657
|
-
if (ms === null) {
|
|
29658
|
-
await ctx.answerCallbackQuery({ text: "bad ttl token" });
|
|
29659
|
-
return;
|
|
29660
|
-
}
|
|
29661
|
-
ttl_ms = ms;
|
|
29662
|
-
displayMode = `granted for ${parsed.choice.param}`;
|
|
29663
|
-
break;
|
|
29664
|
-
}
|
|
29665
|
-
}
|
|
29666
|
-
const consumed = await approvalConsume2(parsed.request_id);
|
|
29667
|
-
if (consumed === null) {
|
|
29668
|
-
await ctx.answerCallbackQuery({ text: "approval kernel unreachable" });
|
|
29669
|
-
return;
|
|
29670
|
-
}
|
|
29671
|
-
if (!consumed.consumed) {
|
|
29672
|
-
await ctx.answerCallbackQuery({ text: "this prompt expired" });
|
|
29664
|
+
const resolved = resolveApprovalDecision(parsed.choice);
|
|
29665
|
+
if (!resolved.ok) {
|
|
29666
|
+
await ctx.answerCallbackQuery({ text: resolved.error });
|
|
29673
29667
|
return;
|
|
29674
29668
|
}
|
|
29669
|
+
const { decision, granted, ttl_ms, displayMode } = resolved;
|
|
29675
29670
|
const granted_by_user_id = ctx.from?.id ?? 0;
|
|
29676
29671
|
const approver_set = [String(granted_by_user_id)];
|
|
29677
|
-
const
|
|
29672
|
+
const result = await approvalConsumeRecord({
|
|
29678
29673
|
request_id: parsed.request_id,
|
|
29679
29674
|
decision,
|
|
29680
29675
|
approver_set,
|
|
29681
29676
|
granted_by_user_id,
|
|
29682
29677
|
ttl_ms
|
|
29683
29678
|
});
|
|
29684
|
-
if (
|
|
29679
|
+
if (result === null) {
|
|
29680
|
+
await ctx.answerCallbackQuery({ text: "approval kernel unreachable" });
|
|
29681
|
+
return;
|
|
29682
|
+
}
|
|
29683
|
+
if (!result.consumed) {
|
|
29684
|
+
await ctx.answerCallbackQuery({ text: "this prompt expired" });
|
|
29685
|
+
return;
|
|
29686
|
+
}
|
|
29687
|
+
if (!result.decision_id) {
|
|
29685
29688
|
await ctx.answerCallbackQuery({ text: "kernel record failed" });
|
|
29686
29689
|
return;
|
|
29687
29690
|
}
|
|
29691
|
+
const decision_id = result.decision_id;
|
|
29688
29692
|
const icon = granted ? "\u2705" : "\uD83D\uDEAB";
|
|
29689
29693
|
const newBody = `${icon} ${displayMode}` + (granted ? ` \u00b7 /approvals revoke <code>${decision_id}</code>` : "");
|
|
29690
|
-
const postTapKeyboard = granted &&
|
|
29694
|
+
const postTapKeyboard = granted && result.scope ? buildGrantedKeyboard(result.scope) : undefined;
|
|
29691
29695
|
try {
|
|
29692
29696
|
await ctx.editMessageText(newBody, {
|
|
29693
29697
|
parse_mode: "HTML",
|
|
@@ -41300,7 +41304,7 @@ async function approvalRecord(args, opts) {
|
|
|
41300
41304
|
return null;
|
|
41301
41305
|
if (!("decision_id" in r.resp))
|
|
41302
41306
|
return null;
|
|
41303
|
-
return r.resp.decision_id;
|
|
41307
|
+
return r.resp.decision_id ?? null;
|
|
41304
41308
|
}
|
|
41305
41309
|
|
|
41306
41310
|
// quota-check.ts
|
|
@@ -43342,6 +43346,26 @@ function escapeHtml7(s) {
|
|
|
43342
43346
|
|
|
43343
43347
|
// gateway/pending-inbound-buffer.ts
|
|
43344
43348
|
var DEFAULT_PENDING_INBOUND_CAP = 32;
|
|
43349
|
+
function redeliverBufferedInbound(buffer, agent, send) {
|
|
43350
|
+
const pending = buffer.drain(agent);
|
|
43351
|
+
let redelivered = 0;
|
|
43352
|
+
let rebuffered = 0;
|
|
43353
|
+
for (const msg of pending) {
|
|
43354
|
+
let delivered = false;
|
|
43355
|
+
try {
|
|
43356
|
+
delivered = send(msg);
|
|
43357
|
+
} catch {
|
|
43358
|
+
delivered = false;
|
|
43359
|
+
}
|
|
43360
|
+
if (delivered) {
|
|
43361
|
+
redelivered++;
|
|
43362
|
+
} else {
|
|
43363
|
+
buffer.push(agent, msg);
|
|
43364
|
+
rebuffered++;
|
|
43365
|
+
}
|
|
43366
|
+
}
|
|
43367
|
+
return { drained: pending.length, redelivered, rebuffered };
|
|
43368
|
+
}
|
|
43345
43369
|
function createPendingInboundBuffer(opts = {}) {
|
|
43346
43370
|
const cap = opts.capPerAgent ?? DEFAULT_PENDING_INBOUND_CAP;
|
|
43347
43371
|
const log = opts.log ?? ((line) => process.stderr.write(line));
|
|
@@ -46744,11 +46768,11 @@ function sweepStaleTurnActiveMarker(stateDir, opts) {
|
|
|
46744
46768
|
}
|
|
46745
46769
|
|
|
46746
46770
|
// ../src/build-info.ts
|
|
46747
|
-
var VERSION = "0.12.
|
|
46748
|
-
var COMMIT_SHA = "
|
|
46749
|
-
var COMMIT_DATE = "2026-05-
|
|
46750
|
-
var LATEST_PR =
|
|
46751
|
-
var COMMITS_AHEAD_OF_TAG =
|
|
46771
|
+
var VERSION = "0.12.15";
|
|
46772
|
+
var COMMIT_SHA = "dc508a92";
|
|
46773
|
+
var COMMIT_DATE = "2026-05-19T07:24:41Z";
|
|
46774
|
+
var LATEST_PR = 1547;
|
|
46775
|
+
var COMMITS_AHEAD_OF_TAG = 22;
|
|
46752
46776
|
|
|
46753
46777
|
// gateway/boot-version.ts
|
|
46754
46778
|
function formatRelativeAgo(iso) {
|
|
@@ -48438,7 +48462,9 @@ startTimer({
|
|
|
48438
48462
|
try {
|
|
48439
48463
|
clearSilentEndState(fbKey);
|
|
48440
48464
|
} catch {}
|
|
48441
|
-
|
|
48465
|
+
const fbSelfAgent = process.env.SWITCHROOM_AGENT_NAME ?? "";
|
|
48466
|
+
const fbRedeliver = redeliverBufferedInbound(pendingInboundBuffer, fbSelfAgent, (m) => ipcServer.sendToAgent(fbSelfAgent, m));
|
|
48467
|
+
process.stderr.write(`telegram gateway: silence-poke framework-fallback ended wedged turn chat=${fbChatId} thread=${ctx.threadId ?? "-"} silence_ms=${ctx.silenceMs} currentTurn_nulled=${turnMatchesFallback} drained_buffered=${fbRedeliver.redelivered}/${fbRedeliver.drained}${fbRedeliver.rebuffered > 0 ? ` rebuffered=${fbRedeliver.rebuffered}` : ""}
|
|
48442
48468
|
`);
|
|
48443
48469
|
}
|
|
48444
48470
|
});
|
|
@@ -12,7 +12,10 @@
|
|
|
12
12
|
|
|
13
13
|
import { describe, expect, it } from "vitest";
|
|
14
14
|
import { InlineKeyboard } from "grammy";
|
|
15
|
-
import {
|
|
15
|
+
import {
|
|
16
|
+
buildGrantedKeyboard,
|
|
17
|
+
resolveApprovalDecision,
|
|
18
|
+
} from "./approval-callback.js";
|
|
16
19
|
|
|
17
20
|
/**
|
|
18
21
|
* Helper — pull the `[{text, url}]` rows out of a grammy InlineKeyboard
|
|
@@ -102,3 +105,48 @@ describe("buildGrantedKeyboard — no button cases", () => {
|
|
|
102
105
|
expect(buildGrantedKeyboard("doc:gdrive:write:abc?evil=1")).toBeUndefined();
|
|
103
106
|
});
|
|
104
107
|
});
|
|
108
|
+
|
|
109
|
+
describe("resolveApprovalDecision — pure decision resolution (PR-5)", () => {
|
|
110
|
+
it("deny → deny / not granted", () => {
|
|
111
|
+
expect(resolveApprovalDecision({ kind: "deny" })).toEqual({
|
|
112
|
+
ok: true, decision: "deny", granted: false, ttl_ms: null, displayMode: "denied",
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it("once → allow_once, no ttl", () => {
|
|
117
|
+
expect(resolveApprovalDecision({ kind: "once" })).toEqual({
|
|
118
|
+
ok: true, decision: "allow_once", granted: true, ttl_ms: null, displayMode: "granted once",
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("always → allow_always, no ttl", () => {
|
|
123
|
+
expect(resolveApprovalDecision({ kind: "always" })).toEqual({
|
|
124
|
+
ok: true, decision: "allow_always", granted: true, ttl_ms: null, displayMode: "granted always",
|
|
125
|
+
});
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it("ttl with a valid token → allow_ttl with computed ms", () => {
|
|
129
|
+
expect(resolveApprovalDecision({ kind: "ttl", param: "24h" })).toEqual({
|
|
130
|
+
ok: true,
|
|
131
|
+
decision: "allow_ttl",
|
|
132
|
+
granted: true,
|
|
133
|
+
ttl_ms: 24 * 60 * 60 * 1000,
|
|
134
|
+
displayMode: "granted for 24h",
|
|
135
|
+
});
|
|
136
|
+
expect(resolveApprovalDecision({ kind: "ttl", param: "7d" })).toMatchObject({
|
|
137
|
+
ok: true, decision: "allow_ttl", ttl_ms: 7 * 24 * 60 * 60 * 1000,
|
|
138
|
+
});
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it("ttl with a bad token → { ok: false } so the caller does NOT consume the nonce", () => {
|
|
142
|
+
// This is the load-bearing case: pre-PR-4 this branch ran after
|
|
143
|
+
// approvalConsume() burned the single-use nonce, wedging the agent.
|
|
144
|
+
// It must report failure WITHOUT side effects (pure fn → caller
|
|
145
|
+
// returns before approvalConsume).
|
|
146
|
+
for (const param of ["bogus", "0h", "1w", "", "h", "12"]) {
|
|
147
|
+
expect(resolveApprovalDecision({ kind: "ttl", param })).toEqual({
|
|
148
|
+
ok: false, error: "bad ttl token",
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
});
|
|
152
|
+
});
|