@north-light/crouter 0.3.314 → 0.3.315
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.
|
@@ -1,3 +1,5 @@
|
|
|
1
1
|
import type { PageAnswer } from './answer.js';
|
|
2
|
-
/** Render a PageAnswer as the deterministic Markdown report delivered to its asking agent.
|
|
3
|
-
|
|
2
|
+
/** Render a PageAnswer as the deterministic Markdown report delivered to its asking agent.
|
|
3
|
+
* The person sees this same prose in their own chat surface, so it carries the answer
|
|
4
|
+
* itself and nothing addressed only to the agent. */
|
|
5
|
+
export declare function renderAnswerText(answer: PageAnswer): string;
|
|
@@ -80,19 +80,21 @@ function slotLines(slot, totalSlots) {
|
|
|
80
80
|
lines.push(...commentLines(comment));
|
|
81
81
|
return lines;
|
|
82
82
|
}
|
|
83
|
-
/** Render a PageAnswer as the deterministic Markdown report delivered to its asking agent.
|
|
84
|
-
|
|
83
|
+
/** Render a PageAnswer as the deterministic Markdown report delivered to its asking agent.
|
|
84
|
+
* The person sees this same prose in their own chat surface, so it carries the answer
|
|
85
|
+
* itself and nothing addressed only to the agent. */
|
|
86
|
+
export function renderAnswerText(answer) {
|
|
85
87
|
if (answer.kind === 'acknowledgement') {
|
|
86
88
|
return [
|
|
87
89
|
`The user saw **${answer.title}** and dismissed it. The page asked for nothing, so the acknowledgement is the whole answer.`,
|
|
88
90
|
'',
|
|
89
|
-
`Acknowledged ${answer.completedAt}
|
|
91
|
+
`Acknowledged ${answer.completedAt}.`,
|
|
90
92
|
].join('\n');
|
|
91
93
|
}
|
|
92
94
|
const answered = answer.slots.filter((slot) => slot.state === 'answered');
|
|
93
95
|
const lines = [`The user answered **${answer.title}**.`];
|
|
94
96
|
for (const slot of answered)
|
|
95
97
|
lines.push('', ...slotLines(slot, answer.slots.length));
|
|
96
|
-
lines.push('', `Answered ${answer.completedAt}
|
|
98
|
+
lines.push('', `Answered ${answer.completedAt}.`);
|
|
97
99
|
return lines.join('\n');
|
|
98
100
|
}
|
|
@@ -2,6 +2,11 @@ import { getRequest, getRequestByInboxTicket } from '../../core/human/requests.j
|
|
|
2
2
|
import { buildPageSummary } from '../../core/human/summary.js';
|
|
3
3
|
import { validateFinalPageResponses } from '../../core/human/tickets.js';
|
|
4
4
|
import { composeHumanCompletion } from '../../core/human/completion.js';
|
|
5
|
+
import { describePageAnswer } from '../../core/human/answer.js';
|
|
6
|
+
import { renderAnswerText } from '../../core/human/answer-text.js';
|
|
7
|
+
import { readPageFeedback } from '../../core/human/feedback.js';
|
|
8
|
+
import { pageDocumentSha256 } from '../../core/human/feedback-companion.js';
|
|
9
|
+
import { pagePath } from '../../core/human/convention.js';
|
|
5
10
|
import { retireCompanion } from '../companion-retire.js';
|
|
6
11
|
import { settleHumanRequest } from './settle.js';
|
|
7
12
|
function requestForTicket(ticketId) {
|
|
@@ -38,10 +43,73 @@ function completionAction(request, result) {
|
|
|
38
43
|
throw new Error(`corrupt frozen completion action: ${request.request_id}`, { cause: error });
|
|
39
44
|
}
|
|
40
45
|
}
|
|
41
|
-
function
|
|
46
|
+
function firstLine(text, maxLen = 80) {
|
|
47
|
+
const line = text.split('\n')[0] ?? '';
|
|
48
|
+
return line.length > maxLen ? `${line.slice(0, maxLen - 1)}…` : line;
|
|
49
|
+
}
|
|
50
|
+
function pageTitle(request) {
|
|
51
|
+
try {
|
|
52
|
+
return manifest(request).title;
|
|
53
|
+
}
|
|
54
|
+
catch {
|
|
55
|
+
return undefined;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
/** The settle-time feedback addendum — answer and cancellation alike: a change
|
|
59
|
+
* notice when the final page differs from the version first commented on, and
|
|
60
|
+
* each still-open item's quote and note. Silent when the page is unchanged and
|
|
61
|
+
* nothing remains open; never a manufactured prose diff. */
|
|
62
|
+
function feedbackAddendum(request) {
|
|
63
|
+
let sections;
|
|
64
|
+
try {
|
|
65
|
+
const feedback = readPageFeedback(request.request_dir);
|
|
66
|
+
if (feedback.companionNodeId === undefined)
|
|
67
|
+
return '';
|
|
68
|
+
sections = [];
|
|
69
|
+
if (feedback.firstCommentedSha256 !== undefined && pageDocumentSha256(request.request_dir) !== feedback.firstCommentedSha256) {
|
|
70
|
+
sections.push(`The page changed during feedback: the person commented on it and its companion conversation (node ${feedback.companionNodeId}) revised it in place. ` +
|
|
71
|
+
`Reread the final page at ${pagePath(request.request_dir)} before interpreting this outcome.`);
|
|
72
|
+
}
|
|
73
|
+
const open = feedback.comments.filter((comment) => comment.status === 'open');
|
|
74
|
+
if (open.length > 0) {
|
|
75
|
+
const items = open.map((comment) => [
|
|
76
|
+
...comment.quote.split('\n').map((line) => `> ${line}`),
|
|
77
|
+
comment.note,
|
|
78
|
+
].join('\n'));
|
|
79
|
+
sections.push([
|
|
80
|
+
`Feedback left unresolved at settlement — the companion conversation (node ${feedback.companionNodeId}) had not dealt with these items:`,
|
|
81
|
+
...items,
|
|
82
|
+
].join('\n\n'));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// The outcome itself is the deliverable; a broken feedback record adds nothing.
|
|
87
|
+
return '';
|
|
88
|
+
}
|
|
89
|
+
return sections.length === 0 ? '' : `\n\n${sections.join('\n\n')}`;
|
|
90
|
+
}
|
|
91
|
+
/** The person's answer as prose the asking agent reads directly. Short bodies
|
|
92
|
+
* ride inline; the mail layer spills a long one to a file and leaves a preview
|
|
93
|
+
* plus its path. */
|
|
94
|
+
function answerBody(request, result) {
|
|
95
|
+
const answer = describePageAnswer(manifest(request), result.responses, { completedAt: result.completedAt });
|
|
96
|
+
return renderAnswerText(answer) + feedbackAddendum(request);
|
|
97
|
+
}
|
|
98
|
+
/** A dismissal is a rejection, not a routing event: say so plainly so the agent
|
|
99
|
+
* acts on the "no" instead of re-raising the same ask. */
|
|
100
|
+
function cancelBody(request, dismissed, reason) {
|
|
101
|
+
const title = pageTitle(request);
|
|
102
|
+
const named = title === undefined ? `the human interaction ${request.request_id}` : `“${title}”`;
|
|
103
|
+
const why = reason !== undefined && reason !== '' ? reason : 'no reason was given';
|
|
104
|
+
const body = dismissed
|
|
105
|
+
? `The user dismissed ${named} without answering — a deliberate “no, I'm not answering this.” Treat it as a rejection: no response will arrive, no partial answer exists, and the same ask must not be raised again.`
|
|
106
|
+
: `${title === undefined ? `The human interaction ${request.request_id}` : `The page “${title}”`} was canceled — ${why}. No response will arrive, and any partial answers the user had entered were discarded unsent.`;
|
|
107
|
+
return body + feedbackAddendum(request);
|
|
108
|
+
}
|
|
109
|
+
function notification(request, disposition, body, result) {
|
|
42
110
|
if (request.requester_node_id === null)
|
|
43
111
|
return undefined;
|
|
44
|
-
return { label:
|
|
112
|
+
return { label: firstLine(body), disposition, body_json: { body, result } };
|
|
45
113
|
}
|
|
46
114
|
function retire(request) {
|
|
47
115
|
return request.companion_node_id === null ? undefined : { retire: async (nodeId, outcome) => { await retireCompanion(nodeId, outcome); } };
|
|
@@ -60,7 +128,9 @@ export async function resolvePageTicket(ticketId, responses, opts = {}) {
|
|
|
60
128
|
request_id: request.request_id,
|
|
61
129
|
outcome: 'answered',
|
|
62
130
|
result_json: JSON.stringify(result),
|
|
63
|
-
...(notification(request, 'human-answer',
|
|
131
|
+
...(notification(request, 'human-answer', answerBody(request, result), result) === undefined
|
|
132
|
+
? {}
|
|
133
|
+
: { notification: notification(request, 'human-answer', answerBody(request, result), result) }),
|
|
64
134
|
...(completionAction(request, result) === undefined ? {} : { completion_action: completionAction(request, result) }),
|
|
65
135
|
...(retire(request) === undefined ? {} : { post_commit: retire(request) }),
|
|
66
136
|
});
|
|
@@ -84,7 +154,9 @@ export async function cancelHumanTicket(ticketId, opts) {
|
|
|
84
154
|
request_id: request.request_id,
|
|
85
155
|
outcome: opts.disposition === 'dismissed' ? 'dismissed' : 'canceled',
|
|
86
156
|
result_json: JSON.stringify(result),
|
|
87
|
-
...(notification(request, 'human-canceled',
|
|
157
|
+
...(notification(request, 'human-canceled', cancelBody(request, opts.disposition === 'dismissed', opts.reason), result) === undefined
|
|
158
|
+
? {}
|
|
159
|
+
: { notification: notification(request, 'human-canceled', cancelBody(request, opts.disposition === 'dismissed', opts.reason), result) }),
|
|
88
160
|
...(completionAction(request, result) === undefined ? {} : { completion_action: completionAction(request, result) }),
|
|
89
161
|
...(retire(request) === undefined ? {} : { post_commit: retire(request) }),
|
|
90
162
|
});
|
package/package.json
CHANGED
package/runtime.lock.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@north-light/crouter",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.315",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@north-light/crouter",
|
|
9
|
-
"version": "0.3.
|
|
9
|
+
"version": "0.3.315",
|
|
10
10
|
"hasInstallScript": true,
|
|
11
11
|
"license": "MIT",
|
|
12
12
|
"workspaces": [
|
|
@@ -5206,17 +5206,17 @@
|
|
|
5206
5206
|
},
|
|
5207
5207
|
"packages/crouter-api": {
|
|
5208
5208
|
"name": "@north-light/crouter-api",
|
|
5209
|
-
"version": "0.3.
|
|
5209
|
+
"version": "0.3.315",
|
|
5210
5210
|
"license": "UNLICENSED"
|
|
5211
5211
|
},
|
|
5212
5212
|
"packages/crouter-env-docker": {
|
|
5213
5213
|
"name": "@north-light/crouter-env-docker",
|
|
5214
|
-
"version": "0.3.
|
|
5214
|
+
"version": "0.3.315",
|
|
5215
5215
|
"license": "UNLICENSED"
|
|
5216
5216
|
},
|
|
5217
5217
|
"packages/crouter-sdk": {
|
|
5218
5218
|
"name": "@north-light/crouter-sdk",
|
|
5219
|
-
"version": "0.3.
|
|
5219
|
+
"version": "0.3.315",
|
|
5220
5220
|
"license": "UNLICENSED",
|
|
5221
5221
|
"dependencies": {
|
|
5222
5222
|
"@north-light/crouter-api": "^0.3.295"
|