@parall/parel-channel 1.55.3 → 1.55.5
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/channel-prompt.d.ts +5 -0
- package/dist/channel-prompt.d.ts.map +1 -1
- package/dist/channel-prompt.js +25 -0
- package/dist/inbound.d.ts.map +1 -1
- package/dist/inbound.js +1 -0
- package/dist/typed-dispatch.d.ts.map +1 -1
- package/dist/typed-dispatch.js +9 -0
- package/package.json +1 -1
- package/src/channel-prompt.ts +36 -0
- package/src/inbound.ts +1 -0
- package/src/typed-dispatch.ts +12 -0
package/dist/channel-prompt.d.ts
CHANGED
|
@@ -38,6 +38,11 @@ export interface ChatPromptArgs {
|
|
|
38
38
|
chatName?: string;
|
|
39
39
|
/** Sender user id (usr_); display-name enrichment is a follow-up. */
|
|
40
40
|
senderId?: string;
|
|
41
|
+
/**
|
|
42
|
+
* The inbound message id (msg_); anchors the forwarded-message hint's
|
|
43
|
+
* explicit --from so coalesced turns don't lean on the single trigger id.
|
|
44
|
+
*/
|
|
45
|
+
messageId?: string;
|
|
41
46
|
threadRootId?: string;
|
|
42
47
|
/** The inbound message text (raw, un-framed); may be empty (attachment-only). */
|
|
43
48
|
text: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channel-prompt.d.ts","sourceRoot":"","sources":["../src/channel-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;
|
|
1
|
+
{"version":3,"file":"channel-prompt.d.ts","sourceRoot":"","sources":["../src/channel-prompt.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AA+BH,MAAM,WAAW,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,yEAAyE;IACzE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,iFAAiF;IACjF,IAAI,EAAE,MAAM,CAAC;IACb,iFAAiF;IACjF,WAAW,CAAC,EAAE,gBAAgB,EAAE,CAAC;IACjC,8FAA8F;IAC9F,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAmBD;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAoB5D;AAED,MAAM,WAAW,iBAAiB;IAChC,gEAAgE;IAChE,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,iDAAiD;IACjD,IAAI,EAAE,MAAM,CAAC;IACb;;;;;OAKG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;CACtB;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,CAkBlE"}
|
package/dist/channel-prompt.js
CHANGED
|
@@ -32,6 +32,24 @@ function sanitizeMeta(value) {
|
|
|
32
32
|
.replace(/[[\]|]/g, ' ')
|
|
33
33
|
.trim();
|
|
34
34
|
}
|
|
35
|
+
const MESSAGE_REF_URI = 'prll:\\/\\/msg_[A-Za-z0-9_-]+(?:\\/content\\?v=[1-9][0-9]{0,8}#t=[0-9]+-[0-9]+)?';
|
|
36
|
+
const MESSAGE_RANGE_REF_URI = 'prll:\\/\\/cht_[A-Za-z0-9_-]+#range=msg_[A-Za-z0-9_-]+,msg_[A-Za-z0-9_-]+';
|
|
37
|
+
const REF_URI = `(?:${MESSAGE_REF_URI}|${MESSAGE_RANGE_REF_URI})`;
|
|
38
|
+
// Mirrors the web forward-card rule (ts/app/src/lib/messageRefParagraph.ts):
|
|
39
|
+
// human clients render a paragraph made only of message refs as forward
|
|
40
|
+
// cards, whether each ref is a bare URI, a markdown link wrapping one
|
|
41
|
+
// ([ctx](prll://…), label may be empty), or an autolink (<prll://…>).
|
|
42
|
+
// Shared vectors pin all three readers:
|
|
43
|
+
// ts/protocol-vectors/forwarded-message-bodies.json.
|
|
44
|
+
const REF_TOKEN = `(?:${REF_URI}|\\[(?:[^\\[\\]]|\\[[^\\[\\]]*\\])*\\]\\(${REF_URI}\\)|<${REF_URI}>)`;
|
|
45
|
+
const FORWARDED_REF_LINE = new RegExp(`^${REF_TOKEN}(?:\\s+${REF_TOKEN})*$`);
|
|
46
|
+
function isForwardedMessageBody(body) {
|
|
47
|
+
const lines = body
|
|
48
|
+
.split(/\r?\n/)
|
|
49
|
+
.map((line) => line.trim())
|
|
50
|
+
.filter(Boolean);
|
|
51
|
+
return lines.length > 0 && lines.every((line) => FORWARDED_REF_LINE.test(line));
|
|
52
|
+
}
|
|
35
53
|
/**
|
|
36
54
|
* One reference line per attachment, byte-identical to agent-core's
|
|
37
55
|
* event-format rendering: the agent learns the attachment EXISTS and pulls
|
|
@@ -65,6 +83,13 @@ export function buildChatPrompt(args) {
|
|
|
65
83
|
lines.push(`[Thread: ${sanitizeMeta(args.threadRootId)}]`);
|
|
66
84
|
if (args.noReply)
|
|
67
85
|
lines.push(`[Hint: no_reply]`);
|
|
86
|
+
if (isForwardedMessageBody(args.text)) {
|
|
87
|
+
// Embed the concrete --from anchor: parel snapshots one invocation
|
|
88
|
+
// context per coalesced turn, so each event's hint must carry its own
|
|
89
|
+
// forwarding grant instead of leaning on the single trigger id.
|
|
90
|
+
const from = args.messageId ? ` --from ${sanitizeMeta(`prll://${args.messageId}`)}` : '';
|
|
91
|
+
lines.push(`[Hint: forwarded_message — run parall refs resolve --full${from} with the prll:// references below; that message carries the cross-chat forwarding access for these refs.]`);
|
|
92
|
+
}
|
|
68
93
|
lines.push(...attachmentLines(args.attachments));
|
|
69
94
|
lines.push('', args.text);
|
|
70
95
|
return lines.join('\n');
|
package/dist/inbound.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inbound.d.ts","sourceRoot":"","sources":["../src/inbound.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,cAAc,EACf,MAAM,mBAAmB,CAAC;AAsI3B,wBAAgB,UAAU,CAAC,CAAC,EAAE,GAAG,EAAE,gBAAgB,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAWrF;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,eAAe,EAAE,CAAC,CAE5B;AAoDD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,EACtB,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,eAAe,EAAE,CAAC,CAE5B;AAsCD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAElF;
|
|
1
|
+
{"version":3,"file":"inbound.d.ts","sourceRoot":"","sources":["../src/inbound.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,gBAAgB,EAChB,gBAAgB,EAChB,eAAe,EACf,cAAc,EACf,MAAM,mBAAmB,CAAC;AAsI3B,wBAAgB,UAAU,CAAC,CAAC,EAAE,GAAG,EAAE,gBAAgB,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAWrF;AAED,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,cAAc,EACrB,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,eAAe,EAAE,CAAC,CAE5B;AAoDD;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE;IAAE,GAAG,EAAE,MAAM,CAAA;CAAE,EACtB,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,eAAe,EAAE,CAAC,CAE5B;AAsCD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,gBAAgB,GAAG,OAAO,CAAC,eAAe,EAAE,CAAC,CAElF;AA0oBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,UAAU,EACjB,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAE7B"}
|
package/dist/inbound.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"typed-dispatch.d.ts","sourceRoot":"","sources":["../src/typed-dispatch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAI1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,MAAM,WAAW,iBAAiB;IAChC,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,mFAAmF;AACnF,eAAO,MAAM,iBAAiB,kIAQpB,CAAC;AAEX,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAE3D;AAED,wEAAwE;AACxE,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,UAAU,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAChD,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,mBAAmB,GAC3B,cAAc;AAChB,2CAA2C;GACzC;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE;AACtB;oEACoE;GAClE;IAAE,OAAO,EAAE,SAAS,CAAA;CAAE,CAAC;
|
|
1
|
+
{"version":3,"file":"typed-dispatch.d.ts","sourceRoot":"","sources":["../src/typed-dispatch.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAI1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,MAAM,WAAW,iBAAiB;IAChC,EAAE,CAAC,EAAE,OAAO,CAAC;IACb,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,eAAe,CAAC,EAAE,OAAO,CAAC;CAC3B;AAED,mFAAmF;AACnF,eAAO,MAAM,iBAAiB,kIAQpB,CAAC;AAEX,wBAAgB,gBAAgB,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAE3D;AAED,wEAAwE;AACxE,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,MAAM,CAAC;IAChB,6EAA6E;IAC7E,OAAO,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,IAAI,EAAE,MAAM,CAAC;IACb,sEAAsE;IACtE,UAAU,EAAE;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAChD,+BAA+B;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,MAAM,mBAAmB,GAC3B,cAAc;AAChB,2CAA2C;GACzC;IAAE,OAAO,EAAE,OAAO,CAAA;CAAE;AACtB;oEACoE;GAClE;IAAE,OAAO,EAAE,SAAS,CAAA;CAAE,CAAC;AA0B3B;;;;;GAKG;AACH,wBAAsB,iBAAiB,CACrC,IAAI,EAAE,iBAAiB,EACvB,GAAG,EAAE,gBAAgB,GACpB,OAAO,CAAC,mBAAmB,CAAC,CAmC9B"}
|
package/dist/typed-dispatch.js
CHANGED
|
@@ -25,6 +25,12 @@ function sanitizeMeta(value) {
|
|
|
25
25
|
.replace(/[[\]|]/g, ' ')
|
|
26
26
|
.trim();
|
|
27
27
|
}
|
|
28
|
+
function formatApprovalDecisionReason(status, reason) {
|
|
29
|
+
const normalized = reason?.trim();
|
|
30
|
+
if (status !== 'rejected' || !normalized)
|
|
31
|
+
return '';
|
|
32
|
+
return `Reason (JSON): ${JSON.stringify(normalized)}`;
|
|
33
|
+
}
|
|
28
34
|
/**
|
|
29
35
|
* Build the plan for one typed dispatch. Throws on transient failures
|
|
30
36
|
* (including unexpected handler errors — a poisoned row surfaces as catch-up
|
|
@@ -344,6 +350,9 @@ async function planApprovalDecided(ctx, approvalId, chatId, actor) {
|
|
|
344
350
|
lines.push(`[Chat: prll://${resolvedChat}]`);
|
|
345
351
|
lines.push(`[Decided by: ${sanitizeMeta(actor ?? approval.decided_by ?? 'system')}]`);
|
|
346
352
|
lines.push('', `${statusLabel}: ${approval.title ?? ''}${execInfo}`);
|
|
353
|
+
const reasonLine = formatApprovalDecisionReason(approval.status, approval.decision_reason);
|
|
354
|
+
if (reasonLine)
|
|
355
|
+
lines.push(reasonLine);
|
|
347
356
|
return {
|
|
348
357
|
outcome: 'emit',
|
|
349
358
|
// The decision belongs to the chat conversation it happened in — group
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@parall/parel-channel",
|
|
3
|
-
"version": "1.55.
|
|
3
|
+
"version": "1.55.5",
|
|
4
4
|
"description": "Parall channel plugin for the parel runtime — lets a parel agent treat Parall as a managed_ws channel (receive dispatches, reply via the Parall API)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
package/src/channel-prompt.ts
CHANGED
|
@@ -34,6 +34,28 @@ function sanitizeMeta(value: string): string {
|
|
|
34
34
|
.trim();
|
|
35
35
|
}
|
|
36
36
|
|
|
37
|
+
const MESSAGE_REF_URI =
|
|
38
|
+
'prll:\\/\\/msg_[A-Za-z0-9_-]+(?:\\/content\\?v=[1-9][0-9]{0,8}#t=[0-9]+-[0-9]+)?';
|
|
39
|
+
const MESSAGE_RANGE_REF_URI =
|
|
40
|
+
'prll:\\/\\/cht_[A-Za-z0-9_-]+#range=msg_[A-Za-z0-9_-]+,msg_[A-Za-z0-9_-]+';
|
|
41
|
+
const REF_URI = `(?:${MESSAGE_REF_URI}|${MESSAGE_RANGE_REF_URI})`;
|
|
42
|
+
// Mirrors the web forward-card rule (ts/app/src/lib/messageRefParagraph.ts):
|
|
43
|
+
// human clients render a paragraph made only of message refs as forward
|
|
44
|
+
// cards, whether each ref is a bare URI, a markdown link wrapping one
|
|
45
|
+
// ([ctx](prll://…), label may be empty), or an autolink (<prll://…>).
|
|
46
|
+
// Shared vectors pin all three readers:
|
|
47
|
+
// ts/protocol-vectors/forwarded-message-bodies.json.
|
|
48
|
+
const REF_TOKEN = `(?:${REF_URI}|\\[(?:[^\\[\\]]|\\[[^\\[\\]]*\\])*\\]\\(${REF_URI}\\)|<${REF_URI}>)`;
|
|
49
|
+
const FORWARDED_REF_LINE = new RegExp(`^${REF_TOKEN}(?:\\s+${REF_TOKEN})*$`);
|
|
50
|
+
|
|
51
|
+
function isForwardedMessageBody(body: string): boolean {
|
|
52
|
+
const lines = body
|
|
53
|
+
.split(/\r?\n/)
|
|
54
|
+
.map((line) => line.trim())
|
|
55
|
+
.filter(Boolean);
|
|
56
|
+
return lines.length > 0 && lines.every((line) => FORWARDED_REF_LINE.test(line));
|
|
57
|
+
}
|
|
58
|
+
|
|
37
59
|
export interface PromptAttachment {
|
|
38
60
|
id: string;
|
|
39
61
|
fileName: string;
|
|
@@ -47,6 +69,11 @@ export interface ChatPromptArgs {
|
|
|
47
69
|
chatName?: string;
|
|
48
70
|
/** Sender user id (usr_); display-name enrichment is a follow-up. */
|
|
49
71
|
senderId?: string;
|
|
72
|
+
/**
|
|
73
|
+
* The inbound message id (msg_); anchors the forwarded-message hint's
|
|
74
|
+
* explicit --from so coalesced turns don't lean on the single trigger id.
|
|
75
|
+
*/
|
|
76
|
+
messageId?: string;
|
|
50
77
|
threadRootId?: string;
|
|
51
78
|
/** The inbound message text (raw, un-framed); may be empty (attachment-only). */
|
|
52
79
|
text: string;
|
|
@@ -87,6 +114,15 @@ export function buildChatPrompt(args: ChatPromptArgs): string {
|
|
|
87
114
|
if (args.senderId) lines.push(`[From: ${sanitizeMeta(args.senderId)}]`);
|
|
88
115
|
if (args.threadRootId) lines.push(`[Thread: ${sanitizeMeta(args.threadRootId)}]`);
|
|
89
116
|
if (args.noReply) lines.push(`[Hint: no_reply]`);
|
|
117
|
+
if (isForwardedMessageBody(args.text)) {
|
|
118
|
+
// Embed the concrete --from anchor: parel snapshots one invocation
|
|
119
|
+
// context per coalesced turn, so each event's hint must carry its own
|
|
120
|
+
// forwarding grant instead of leaning on the single trigger id.
|
|
121
|
+
const from = args.messageId ? ` --from ${sanitizeMeta(`prll://${args.messageId}`)}` : '';
|
|
122
|
+
lines.push(
|
|
123
|
+
`[Hint: forwarded_message — run parall refs resolve --full${from} with the prll:// references below; that message carries the cross-chat forwarding access for these refs.]`,
|
|
124
|
+
);
|
|
125
|
+
}
|
|
90
126
|
lines.push(...attachmentLines(args.attachments));
|
|
91
127
|
lines.push('', args.text);
|
|
92
128
|
return lines.join('\n');
|
package/src/inbound.ts
CHANGED
package/src/typed-dispatch.ts
CHANGED
|
@@ -106,6 +106,15 @@ function sanitizeMeta(value: string): string {
|
|
|
106
106
|
.trim();
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
+
function formatApprovalDecisionReason(
|
|
110
|
+
status: string | undefined,
|
|
111
|
+
reason: string | null | undefined,
|
|
112
|
+
): string {
|
|
113
|
+
const normalized = reason?.trim();
|
|
114
|
+
if (status !== 'rejected' || !normalized) return '';
|
|
115
|
+
return `Reason (JSON): ${JSON.stringify(normalized)}`;
|
|
116
|
+
}
|
|
117
|
+
|
|
109
118
|
/**
|
|
110
119
|
* Build the plan for one typed dispatch. Throws on transient failures
|
|
111
120
|
* (including unexpected handler errors — a poisoned row surfaces as catch-up
|
|
@@ -501,6 +510,7 @@ interface ApprovalRow {
|
|
|
501
510
|
execution_status?: string;
|
|
502
511
|
chat_id?: string;
|
|
503
512
|
decided_by?: string;
|
|
513
|
+
decision_reason?: string | null;
|
|
504
514
|
}
|
|
505
515
|
|
|
506
516
|
async function planApprovalDecided(
|
|
@@ -530,6 +540,8 @@ async function planApprovalDecided(
|
|
|
530
540
|
if (resolvedChat) lines.push(`[Chat: prll://${resolvedChat}]`);
|
|
531
541
|
lines.push(`[Decided by: ${sanitizeMeta(actor ?? approval.decided_by ?? 'system')}]`);
|
|
532
542
|
lines.push('', `${statusLabel}: ${approval.title ?? ''}${execInfo}`);
|
|
543
|
+
const reasonLine = formatApprovalDecisionReason(approval.status, approval.decision_reason);
|
|
544
|
+
if (reasonLine) lines.push(reasonLine);
|
|
533
545
|
|
|
534
546
|
return {
|
|
535
547
|
outcome: 'emit',
|