crewly 1.20.349 → 1.20.351
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/config/skills/agent/core/complete-task/SKILL.md +6 -5
- package/config/skills/agent/core/complete-task/execute.sh +13 -4
- package/config/skills/agent/core/complete-task/execute.test.sh +7 -0
- package/config/skills/agent/core/report-status/SKILL.md +3 -3
- package/config/skills/agent/core/report-status/execute.sh +9 -5
- package/config/skills/agent/core/trace-read/SKILL.md +14 -0
- package/config/skills/agent/core/trace-read/execute.sh +25 -2
- package/config/skills/orchestrator/complete-task/SKILL.md +1 -1
- package/config/skills/orchestrator/complete-task/execute.sh +3 -2
- package/config/skills/orchestrator/render-verdict/SKILL.md +2 -1
- package/config/skills/orchestrator/render-verdict/execute.sh +8 -4
- package/config/skills/team-leader/verify-output/SKILL.md +8 -3
- package/config/skills/team-leader/verify-output/execute.sh +4 -3
- package/config/skills/team-leader/verify-output/execute.test.sh +2 -0
- package/dist/backend/backend/src/constants.d.ts +30 -5
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +32 -5
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/request/request.controller.d.ts +12 -0
- package/dist/backend/backend/src/controllers/request/request.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/request/request.controller.js +48 -0
- package/dist/backend/backend/src/controllers/request/request.controller.js.map +1 -1
- package/dist/backend/backend/src/controllers/request/request.routes.d.ts +1 -0
- package/dist/backend/backend/src/controllers/request/request.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/request/request.routes.js +4 -1
- package/dist/backend/backend/src/controllers/request/request.routes.js.map +1 -1
- package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.d.ts +8 -5
- package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js +91 -16
- package/dist/backend/backend/src/controllers/task-pool/task-pool.controller.js.map +1 -1
- package/dist/backend/backend/src/services/event-bus/event-to-workitem-bridge.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/event-bus/event-to-workitem-bridge.service.js +3 -1
- package/dist/backend/backend/src/services/event-bus/event-to-workitem-bridge.service.js.map +1 -1
- package/dist/backend/backend/src/services/messaging/agent-ask-ledger.service.d.ts +6 -0
- package/dist/backend/backend/src/services/messaging/agent-ask-ledger.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/messaging/agent-ask-ledger.service.js +9 -0
- package/dist/backend/backend/src/services/messaging/agent-ask-ledger.service.js.map +1 -1
- package/dist/backend/backend/src/services/task-pool/completion-evidence.service.d.ts +74 -5
- package/dist/backend/backend/src/services/task-pool/completion-evidence.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/task-pool/completion-evidence.service.js +187 -21
- package/dist/backend/backend/src/services/task-pool/completion-evidence.service.js.map +1 -1
- package/dist/backend/backend/src/services/v3/request-whereabouts.service.d.ts +88 -0
- package/dist/backend/backend/src/services/v3/request-whereabouts.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/v3/request-whereabouts.service.js +193 -0
- package/dist/backend/backend/src/services/v3/request-whereabouts.service.js.map +1 -0
- package/dist/backend/backend/src/types/v2/completion-evidence.types.d.ts +16 -0
- package/dist/backend/backend/src/types/v2/completion-evidence.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/v2/completion-evidence.types.js.map +1 -1
- package/dist/backend/backend/src/types/v2/work-item.types.d.ts +6 -0
- package/dist/backend/backend/src/types/v2/work-item.types.d.ts.map +1 -1
- package/dist/backend/backend/src/types/v2/work-item.types.js +6 -0
- package/dist/backend/backend/src/types/v2/work-item.types.js.map +1 -1
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +30 -5
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +32 -5
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/services/messaging/agent-ask-ledger.service.d.ts +6 -0
- package/dist/cli/backend/src/services/messaging/agent-ask-ledger.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/messaging/agent-ask-ledger.service.js +9 -0
- package/dist/cli/backend/src/services/messaging/agent-ask-ledger.service.js.map +1 -1
- package/dist/cli/backend/src/types/v2/work-item.types.d.ts +6 -0
- package/dist/cli/backend/src/types/v2/work-item.types.d.ts.map +1 -1
- package/dist/cli/backend/src/types/v2/work-item.types.js +6 -0
- package/dist/cli/backend/src/types/v2/work-item.types.js.map +1 -1
- package/frontend/dist/assets/{index-36b1788c.js → index-b1fc60ea.js} +65 -65
- package/frontend/dist/index.html +1 -1
- package/package.json +1 -1
- package/packages/crewly-agent/src/runtime/tool-registry.test.ts +26 -0
- package/packages/crewly-agent/src/runtime/tool-registry.ts +31 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"completion-evidence.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/task-pool/completion-evidence.service.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"completion-evidence.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/task-pool/completion-evidence.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -5,7 +5,8 @@
|
|
|
5
5
|
* touches the WorkItem. The decision is one of:
|
|
6
6
|
*
|
|
7
7
|
* - `reject` (400): malformed evidence, an artifact path that does not exist
|
|
8
|
-
* (or a relative path with no project/worktree to resolve it against),
|
|
8
|
+
* (or a relative path with no project/worktree to resolve it against), an
|
|
9
|
+
* empty file, a file last modified before the WorkItem was created (#1313), a
|
|
9
10
|
* command that exited non-zero, or — in `enforce` mode — no evidence.
|
|
10
11
|
* - `block`: a `blocked` entry. The worker could not finish, so the item is
|
|
11
12
|
* recorded as blocked (same path as `POST /task-pool/block/:id`), not done.
|
|
@@ -23,6 +24,23 @@ import { fileURLToPath } from 'url';
|
|
|
23
24
|
import { COMPLETION_EVIDENCE_CONSTANTS, WORKTREE_CONSTANTS } from '../../constants.js';
|
|
24
25
|
import { validateCompletionEvidence, } from '../../types/v2/completion-evidence.types.js';
|
|
25
26
|
const C = COMPLETION_EVIDENCE_CONSTANTS;
|
|
27
|
+
/**
|
|
28
|
+
* Whether a WorkItem of this type must carry evidence to complete (#1312).
|
|
29
|
+
* Deliverables (delegate, project_task, review) and items with no type do;
|
|
30
|
+
* harness bookkeeping types (triage, replans, retros, notifications) do not.
|
|
31
|
+
*
|
|
32
|
+
* @param type - The WorkItem's type, if known
|
|
33
|
+
* @returns True when a completion without evidence is refused in enforce mode
|
|
34
|
+
*
|
|
35
|
+
* @example
|
|
36
|
+
* ```typescript
|
|
37
|
+
* evidenceRequiredFor('delegate'); // true
|
|
38
|
+
* evidenceRequiredFor('goal_replan'); // false
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export function evidenceRequiredFor(type) {
|
|
42
|
+
return !type || C.REQUIRED_FOR_TYPES.includes(type);
|
|
43
|
+
}
|
|
26
44
|
/**
|
|
27
45
|
* The rollout mode in effect: `CREWLY_EVIDENCE_MODE` when it is `warn` or
|
|
28
46
|
* `enforce`, else {@link COMPLETION_EVIDENCE_CONSTANTS.EVIDENCE_ENFORCEMENT_MODE}.
|
|
@@ -38,18 +56,18 @@ export function resolveEvidenceEnforcementMode(env = process.env) {
|
|
|
38
56
|
return C.EVIDENCE_ENFORCEMENT_MODE;
|
|
39
57
|
}
|
|
40
58
|
/**
|
|
41
|
-
* Default
|
|
59
|
+
* Default file check.
|
|
42
60
|
*
|
|
43
61
|
* @param p - Absolute path
|
|
44
|
-
* @returns
|
|
62
|
+
* @returns Its stat, or null when nothing exists at `p`
|
|
45
63
|
*/
|
|
46
|
-
async function
|
|
64
|
+
async function defaultStatPath(p) {
|
|
47
65
|
try {
|
|
48
|
-
await fs.stat(p);
|
|
49
|
-
return
|
|
66
|
+
const st = await fs.stat(p);
|
|
67
|
+
return { isDirectory: st.isDirectory(), sizeBytes: st.size, mtimeMs: st.mtimeMs };
|
|
50
68
|
}
|
|
51
69
|
catch {
|
|
52
|
-
return
|
|
70
|
+
return null;
|
|
53
71
|
}
|
|
54
72
|
}
|
|
55
73
|
/**
|
|
@@ -77,12 +95,14 @@ export function artifactBaseDirs(workItem) {
|
|
|
77
95
|
return [...new Set(bases)];
|
|
78
96
|
}
|
|
79
97
|
/**
|
|
80
|
-
* Check one artifact: http(s) URLs pass without fetching; local paths must
|
|
98
|
+
* Check one artifact: http(s) URLs pass without fetching; local paths must
|
|
99
|
+
* exist, and a file must be non-empty and modified no earlier than
|
|
100
|
+
* `notBeforeMs` (#1313). Directories only need to exist.
|
|
81
101
|
*
|
|
82
102
|
* @param artifact - The artifact entry
|
|
83
103
|
* @param bases - Directories for relative paths
|
|
84
|
-
* @param options -
|
|
85
|
-
* @returns ok, or the problem with this artifact
|
|
104
|
+
* @param options - File check, home directory, and the oldest acceptable mtime (null: no age check)
|
|
105
|
+
* @returns ok (with the stat of a local artifact), or the problem with this artifact
|
|
86
106
|
*/
|
|
87
107
|
async function checkArtifact(artifact, bases, options) {
|
|
88
108
|
const raw = artifact.path;
|
|
@@ -112,9 +132,8 @@ async function checkArtifact(artifact, bases, options) {
|
|
|
112
132
|
local = raw;
|
|
113
133
|
}
|
|
114
134
|
if (path.isAbsolute(local)) {
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
: { ok: false, code: C.CODES.ARTIFACT_NOT_FOUND, problem: `"${raw}" does not exist` };
|
|
135
|
+
const st = await options.statPath(local);
|
|
136
|
+
return st ? judgeFile(raw, st, options.notBeforeMs) : { ok: false, code: C.CODES.ARTIFACT_NOT_FOUND, problem: `"${raw}" does not exist` };
|
|
118
137
|
}
|
|
119
138
|
if (bases.length === 0) {
|
|
120
139
|
return {
|
|
@@ -124,8 +143,9 @@ async function checkArtifact(artifact, bases, options) {
|
|
|
124
143
|
};
|
|
125
144
|
}
|
|
126
145
|
for (const base of bases) {
|
|
127
|
-
|
|
128
|
-
|
|
146
|
+
const st = await options.statPath(path.resolve(base, local));
|
|
147
|
+
if (st)
|
|
148
|
+
return judgeFile(raw, st, options.notBeforeMs);
|
|
129
149
|
}
|
|
130
150
|
return {
|
|
131
151
|
ok: false,
|
|
@@ -133,6 +153,40 @@ async function checkArtifact(artifact, bases, options) {
|
|
|
133
153
|
problem: `"${raw}" does not exist (looked in ${bases.join(', ')})`,
|
|
134
154
|
};
|
|
135
155
|
}
|
|
156
|
+
/**
|
|
157
|
+
* An existing local artifact: a file must have content and be newer than the
|
|
158
|
+
* WorkItem (a placeholder or an earlier task's output is not evidence).
|
|
159
|
+
*
|
|
160
|
+
* @param raw - The path as sent
|
|
161
|
+
* @param st - Its stat
|
|
162
|
+
* @param notBeforeMs - Oldest acceptable mtime, or null for no age check
|
|
163
|
+
* @returns The check
|
|
164
|
+
*/
|
|
165
|
+
function judgeFile(raw, st, notBeforeMs) {
|
|
166
|
+
if (st.isDirectory)
|
|
167
|
+
return { ok: true, stat: st };
|
|
168
|
+
if (st.sizeBytes <= 0)
|
|
169
|
+
return { ok: false, code: C.CODES.ARTIFACT_EMPTY, problem: `"${raw}" is empty (0 bytes)` };
|
|
170
|
+
if (notBeforeMs !== null && st.mtimeMs < notBeforeMs) {
|
|
171
|
+
return {
|
|
172
|
+
ok: false,
|
|
173
|
+
code: C.CODES.ARTIFACT_STALE,
|
|
174
|
+
problem: `"${raw}" was last changed ${new Date(st.mtimeMs).toISOString()}, before this WorkItem existed, so it is not this task's output; attach what this task produced, or a command that shows the work`,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
return { ok: true, stat: st };
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* The oldest mtime a file artifact may have: the WorkItem's creation minus
|
|
181
|
+
* {@link COMPLETION_EVIDENCE_CONSTANTS.STALE_GRACE_MS}.
|
|
182
|
+
*
|
|
183
|
+
* @param workItem - The WorkItem (null or no createdAt: no age check)
|
|
184
|
+
* @returns Epoch ms, or null
|
|
185
|
+
*/
|
|
186
|
+
export function artifactNotBefore(workItem) {
|
|
187
|
+
const created = workItem?.createdAt ? Date.parse(workItem.createdAt) : NaN;
|
|
188
|
+
return Number.isFinite(created) ? created - C.STALE_GRACE_MS : null;
|
|
189
|
+
}
|
|
136
190
|
/**
|
|
137
191
|
* The blocked reason recorded on the WorkItem for `blocked` entries.
|
|
138
192
|
*
|
|
@@ -156,11 +210,16 @@ export function describeEvidenceForReviewer(output) {
|
|
|
156
210
|
const v = validateCompletionEvidence(output?.['evidence']);
|
|
157
211
|
const evidence = v.ok ? v.evidence : [];
|
|
158
212
|
if (evidence.length === 0) {
|
|
159
|
-
return 'Evidence: NONE — marked done without artifacts or commands (accepted only in evidence warn mode). Check the work directly or send it back asking for evidence.';
|
|
213
|
+
return 'Evidence: NONE — marked done without artifacts or commands (accepted only in evidence warn mode). Check the work directly (a check with "entry": null) or send it back asking for evidence.';
|
|
160
214
|
}
|
|
161
215
|
const artifacts = evidence.filter((e) => e.type === 'artifact').length;
|
|
162
216
|
const commands = evidence.filter((e) => e.type === 'command').length;
|
|
163
|
-
|
|
217
|
+
// Numbered so the reviewer's checks can name each entry (#1314).
|
|
218
|
+
const lines = evidence.map((e, i) => ` [${i}] ${e.type === 'artifact' ? `artifact ${e.path}` : e.type === 'command' ? `command \`${e.command}\` → exit ${e.exitCode}` : `blocked at ${e.step}`}`.slice(0, 300));
|
|
219
|
+
const shown = lines.slice(0, C.REVIEW_LISTED_ENTRIES);
|
|
220
|
+
if (lines.length > shown.length)
|
|
221
|
+
shown.push(` … and ${lines.length - shown.length} more (verify-output lists them all)`);
|
|
222
|
+
return `Evidence: ${artifacts} artifact(s), ${commands} command(s) — open or re-run them and record each check:\n${shown.join('\n')}`;
|
|
164
223
|
}
|
|
165
224
|
/**
|
|
166
225
|
* Decide what a completion does from its evidence block.
|
|
@@ -189,12 +248,12 @@ export async function decideCompletion(rawEvidence, workItem, options) {
|
|
|
189
248
|
action: 'reject',
|
|
190
249
|
status: 400,
|
|
191
250
|
code: C.CODES.MISSING,
|
|
192
|
-
error: `complete requires evidence: "done" needs at least one artifact that exists or command with its exit code. ${C.SHAPE_HINT}`,
|
|
251
|
+
error: `complete requires evidence: "done" needs at least one artifact that exists or command with its exit code. Nothing was completed. ${C.SHAPE_HINT}`,
|
|
193
252
|
};
|
|
194
253
|
}
|
|
195
254
|
return {
|
|
196
255
|
action: 'complete',
|
|
197
|
-
warning: `Completed WITHOUT evidence.
|
|
256
|
+
warning: `Completed WITHOUT evidence. Accepted only because ${C.ENV_MODE}=warn; by default this is rejected. ${C.SHAPE_HINT}`,
|
|
198
257
|
};
|
|
199
258
|
}
|
|
200
259
|
const validation = validateCompletionEvidence(rawEvidence);
|
|
@@ -215,7 +274,11 @@ export async function decideCompletion(rawEvidence, workItem, options) {
|
|
|
215
274
|
};
|
|
216
275
|
}
|
|
217
276
|
const bases = artifactBaseDirs(workItem);
|
|
218
|
-
const fsOptions = {
|
|
277
|
+
const fsOptions = {
|
|
278
|
+
statPath: options.statPath ?? defaultStatPath,
|
|
279
|
+
homeDir: options.homeDir ?? os.homedir(),
|
|
280
|
+
notBeforeMs: artifactNotBefore(workItem),
|
|
281
|
+
};
|
|
219
282
|
const problems = [];
|
|
220
283
|
for (let i = 0; i < evidence.length; i++) {
|
|
221
284
|
const e = evidence[i];
|
|
@@ -224,15 +287,118 @@ export async function decideCompletion(rawEvidence, workItem, options) {
|
|
|
224
287
|
const check = await checkArtifact(e, bases, fsOptions);
|
|
225
288
|
if (!check.ok)
|
|
226
289
|
problems.push({ code: check.code, text: `evidence[${i}] artifact ${check.problem}` });
|
|
290
|
+
// Recorded so the reviewer sees what was checked (#1313).
|
|
291
|
+
else if (check.stat && !check.stat.isDirectory)
|
|
292
|
+
evidence[i] = { ...e, sizeBytes: check.stat.sizeBytes, mtime: new Date(check.stat.mtimeMs).toISOString() };
|
|
227
293
|
}
|
|
228
294
|
if (problems.length > 0) {
|
|
229
295
|
return {
|
|
230
296
|
action: 'reject',
|
|
231
297
|
status: 400,
|
|
232
298
|
code: problems[0].code,
|
|
233
|
-
error: `${problems.map((p) => p.text).join('; ')}. Artifacts must exist on this machine (or be http(s) URLs).`,
|
|
299
|
+
error: `${problems.map((p) => p.text).join('; ')}. Artifacts must exist on this machine (or be http(s) URLs), and files must be non-empty and made for this task.`,
|
|
234
300
|
};
|
|
235
301
|
}
|
|
236
302
|
return { action: 'complete', evidence };
|
|
237
303
|
}
|
|
304
|
+
/**
|
|
305
|
+
* Why one raw check entry is not a {@link ReviewCheck}, or null when it is.
|
|
306
|
+
*
|
|
307
|
+
* @param raw - One entry of `result.checks`
|
|
308
|
+
* @param evidenceCount - Number of entries in the worker's evidence
|
|
309
|
+
* @returns A problem description, or null
|
|
310
|
+
*/
|
|
311
|
+
function checkProblem(raw, evidenceCount) {
|
|
312
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw))
|
|
313
|
+
return 'must be an object';
|
|
314
|
+
const c = raw;
|
|
315
|
+
const entry = c['entry'];
|
|
316
|
+
if (entry !== null && entry !== undefined) {
|
|
317
|
+
if (typeof entry !== 'number' || !Number.isInteger(entry))
|
|
318
|
+
return '"entry" must be an evidence index or null';
|
|
319
|
+
if (entry < 0 || entry >= evidenceCount)
|
|
320
|
+
return `"entry" ${entry} is not in the worker's evidence (it has ${evidenceCount} entr${evidenceCount === 1 ? 'y' : 'ies'})`;
|
|
321
|
+
}
|
|
322
|
+
if (typeof c['did'] !== 'string' || !c['did'].trim())
|
|
323
|
+
return '"did" must say what you did';
|
|
324
|
+
if (c['did'].length > C.MAX_FIELD_CHARS)
|
|
325
|
+
return `"did" is longer than ${C.MAX_FIELD_CHARS} characters`;
|
|
326
|
+
if (!C.CHECK_OUTCOMES.includes(c['outcome']))
|
|
327
|
+
return '"outcome" must be "pass" or "fail"';
|
|
328
|
+
return null;
|
|
329
|
+
}
|
|
330
|
+
/**
|
|
331
|
+
* Decide whether a reviewer's accept of a worker's delivery may go through
|
|
332
|
+
* (#1314): it must record, per worker evidence entry it looked at, what it
|
|
333
|
+
* did. A rubber-stamp accept was indistinguishable from a real check.
|
|
334
|
+
*
|
|
335
|
+
* Rules (rejections are not affected; only accepts are checked):
|
|
336
|
+
* - `checks` is a non-empty array of `{entry, did, outcome}`;
|
|
337
|
+
* - when the worker sent evidence, at least one check names an entry of it;
|
|
338
|
+
* without evidence, a check with `entry: null` (a direct look) is enough;
|
|
339
|
+
* - a check with `outcome: "fail"` cannot be accepted — send it back.
|
|
340
|
+
*
|
|
341
|
+
* In `warn` mode a missing record is accepted and flagged `unchecked`;
|
|
342
|
+
* malformed or failed checks are refused in both modes.
|
|
343
|
+
*
|
|
344
|
+
* @param rawChecks - `body.result.checks` as received (any value)
|
|
345
|
+
* @param workerEvidence - The worker's `output.evidence` (any value)
|
|
346
|
+
* @param mode - Rollout mode ({@link resolveEvidenceEnforcementMode})
|
|
347
|
+
* @returns The decision; never throws
|
|
348
|
+
*
|
|
349
|
+
* @example
|
|
350
|
+
* ```typescript
|
|
351
|
+
* const d = decideReviewAccept(result.checks, source.output?.evidence, resolveEvidenceEnforcementMode());
|
|
352
|
+
* if (d.action === 'reject') return res.status(d.status).json({ error: d.error, code: d.code });
|
|
353
|
+
* ```
|
|
354
|
+
*/
|
|
355
|
+
export function decideReviewAccept(rawChecks, workerEvidence, mode) {
|
|
356
|
+
const ev = validateCompletionEvidence(workerEvidence);
|
|
357
|
+
const evidenceCount = ev.ok ? ev.evidence.length : 0;
|
|
358
|
+
const missing = rawChecks === undefined || rawChecks === null || (Array.isArray(rawChecks) && rawChecks.length === 0);
|
|
359
|
+
if (missing) {
|
|
360
|
+
const what = evidenceCount > 0 ? `the worker sent ${evidenceCount} evidence entr${evidenceCount === 1 ? 'y' : 'ies'}` : 'the worker sent no evidence';
|
|
361
|
+
if (mode === 'warn') {
|
|
362
|
+
return { action: 'accept_unchecked', warning: `Accepted WITHOUT a check record (${what}); flagged unchecked. ${C.CHECKS_HINT}` };
|
|
363
|
+
}
|
|
364
|
+
return {
|
|
365
|
+
action: 'reject',
|
|
366
|
+
status: 400,
|
|
367
|
+
code: C.CODES.REVIEW_UNCHECKED,
|
|
368
|
+
error: `accepting a review requires a record of what you checked; ${what}. Nothing was accepted. ${C.CHECKS_HINT}`,
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
if (!Array.isArray(rawChecks)) {
|
|
372
|
+
return { action: 'reject', status: 400, code: C.CODES.REVIEW_CHECKS_MALFORMED, error: `checks must be an array. ${C.CHECKS_HINT}` };
|
|
373
|
+
}
|
|
374
|
+
if (rawChecks.length > C.MAX_ENTRIES) {
|
|
375
|
+
return { action: 'reject', status: 400, code: C.CODES.REVIEW_CHECKS_MALFORMED, error: `checks has ${rawChecks.length} entries; at most ${C.MAX_ENTRIES} are accepted` };
|
|
376
|
+
}
|
|
377
|
+
const checks = [];
|
|
378
|
+
for (let i = 0; i < rawChecks.length; i++) {
|
|
379
|
+
const problem = checkProblem(rawChecks[i], evidenceCount);
|
|
380
|
+
if (problem)
|
|
381
|
+
return { action: 'reject', status: 400, code: C.CODES.REVIEW_CHECKS_MALFORMED, error: `checks[${i}]: ${problem}. ${C.CHECKS_HINT}` };
|
|
382
|
+
const c = rawChecks[i];
|
|
383
|
+
checks.push({ entry: typeof c['entry'] === 'number' ? c['entry'] : null, did: c['did'].trim(), outcome: c['outcome'] });
|
|
384
|
+
}
|
|
385
|
+
if (evidenceCount > 0 && !checks.some((c) => c.entry !== null)) {
|
|
386
|
+
return {
|
|
387
|
+
action: 'reject',
|
|
388
|
+
status: 400,
|
|
389
|
+
code: C.CODES.REVIEW_UNCHECKED,
|
|
390
|
+
error: `the worker sent ${evidenceCount} evidence entr${evidenceCount === 1 ? 'y' : 'ies'} but no check names one: open or re-run at least one and record it. ${C.CHECKS_HINT}`,
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
const failed = checks.flatMap((c, i) => (c.outcome === 'fail' ? [`checks[${i}] (${c.did})`] : []));
|
|
394
|
+
if (failed.length > 0) {
|
|
395
|
+
return {
|
|
396
|
+
action: 'reject',
|
|
397
|
+
status: 400,
|
|
398
|
+
code: C.CODES.REVIEW_CHECK_FAILED,
|
|
399
|
+
error: `${failed.join('; ')} failed: a failed check cannot be accepted. Send it back with {"verdict":"rejected","feedback":"<what is wrong>"}.`,
|
|
400
|
+
};
|
|
401
|
+
}
|
|
402
|
+
return { action: 'accept', checks };
|
|
403
|
+
}
|
|
238
404
|
//# sourceMappingURL=completion-evidence.service.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"completion-evidence.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/task-pool/completion-evidence.service.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"completion-evidence.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/task-pool/completion-evidence.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a Request is right now: who holds it, doing what, since when.
|
|
3
|
+
*
|
|
4
|
+
* On 2026-10-14 an owner request went Ella → Pia → Dex, Dex finished, and
|
|
5
|
+
* the result sat in Dex's terminal for seven hours. Reconstructing that took
|
|
6
|
+
* reading three agents' transcripts. Everything needed was already recorded:
|
|
7
|
+
* the Request's open WorkItems (holder = `target`, time = `statusChangedAt`)
|
|
8
|
+
* and the agent ask ledger (a holder that handed the work to another agent
|
|
9
|
+
* with a plain message). This module joins them into one answer.
|
|
10
|
+
*
|
|
11
|
+
* Pure: the caller passes the Request, its WorkItems and the open asks.
|
|
12
|
+
*
|
|
13
|
+
* @module services/v3/request-whereabouts.service
|
|
14
|
+
*/
|
|
15
|
+
import type { Request } from '../../types/v2/request.types.js';
|
|
16
|
+
import type { WorkItem } from '../../types/v2/work-item.types.js';
|
|
17
|
+
import type { AgentAsk } from '../messaging/agent-ask-ledger.service.js';
|
|
18
|
+
/** What a holder is doing with the request. */
|
|
19
|
+
export type HolderState = 'working' | 'queued' | 'blocked' | 'awaiting_review' | 'waiting_on_agent' | 'waiting_on_owner' | 'handling';
|
|
20
|
+
/** One place the request is held. */
|
|
21
|
+
export interface RequestHolder {
|
|
22
|
+
/** Agent session, or `owner` */
|
|
23
|
+
session: string;
|
|
24
|
+
/** What it is doing */
|
|
25
|
+
state: HolderState;
|
|
26
|
+
/** Since when (ISO 8601) */
|
|
27
|
+
since: string;
|
|
28
|
+
/** How long, in ms, at `now` */
|
|
29
|
+
forMs: number;
|
|
30
|
+
/** The WorkItem behind it, when there is one */
|
|
31
|
+
workItemId?: string;
|
|
32
|
+
/** That WorkItem's title, or the ask excerpt */
|
|
33
|
+
what?: string;
|
|
34
|
+
/** Who handed it here (an ask's sender) */
|
|
35
|
+
from?: string;
|
|
36
|
+
/** Why it is blocked */
|
|
37
|
+
blockedReason?: string;
|
|
38
|
+
}
|
|
39
|
+
/** The answer to "where is this request now?". */
|
|
40
|
+
export interface RequestWhereabouts {
|
|
41
|
+
requestId: string;
|
|
42
|
+
ticketNumber?: number;
|
|
43
|
+
title: string;
|
|
44
|
+
status: Request['status'];
|
|
45
|
+
/** True when the request is done or cancelled */
|
|
46
|
+
finished: boolean;
|
|
47
|
+
/** When it finished (ISO), if it did */
|
|
48
|
+
finishedAt?: string;
|
|
49
|
+
/** Where it is held now, longest-held first */
|
|
50
|
+
holders: RequestHolder[];
|
|
51
|
+
/** One line for a person */
|
|
52
|
+
summary: string;
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Format a duration for a person: "45m", "2h 10m", "3d 4h".
|
|
56
|
+
*
|
|
57
|
+
* @param ms - Duration
|
|
58
|
+
* @returns Short text
|
|
59
|
+
*/
|
|
60
|
+
export declare function formatDuration(ms: number): string;
|
|
61
|
+
/**
|
|
62
|
+
* Compute where a request is now.
|
|
63
|
+
*
|
|
64
|
+
* Rules:
|
|
65
|
+
* - finished request → no holders;
|
|
66
|
+
* - each open WorkItem of the request (by `requestId` or `workItemIds`) is a
|
|
67
|
+
* holder: its `target` (running/queued/blocked) or, once the worker is done,
|
|
68
|
+
* its reviewer (awaiting_review). Review items themselves are folded in;
|
|
69
|
+
* - a holder that handed the work to another agent with a plain message (an
|
|
70
|
+
* open ask newer than the holder's own start) is `waiting_on_agent`, and
|
|
71
|
+
* the asked agent is a holder too (followed up to {@link MAX_ASK_HOPS});
|
|
72
|
+
* - no open WorkItem: the request's `assignee`/`ownerAgent` handles it, or the
|
|
73
|
+
* owner when the request waits on a confirmation or follow-up.
|
|
74
|
+
*
|
|
75
|
+
* @param request - The Request
|
|
76
|
+
* @param workItems - WorkItems to search (the pool, or a subset)
|
|
77
|
+
* @param openAsks - Open agent-to-agent asks (from the ledger)
|
|
78
|
+
* @param now - Current time (epoch ms)
|
|
79
|
+
* @returns The whereabouts
|
|
80
|
+
*
|
|
81
|
+
* @example
|
|
82
|
+
* ```typescript
|
|
83
|
+
* const w = computeWhereabouts(request, await pool.getAllItems(), ledger.openAsks(), Date.now());
|
|
84
|
+
* w.summary; // "With Dex (working on "Replicate the video") for 2h 10m"
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
export declare function computeWhereabouts(request: Request, workItems: WorkItem[], openAsks: AgentAsk[], now: number): RequestWhereabouts;
|
|
88
|
+
//# sourceMappingURL=request-whereabouts.service.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"request-whereabouts.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/v3/request-whereabouts.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iCAAiC,CAAC;AAC/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAC;AAElE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,0CAA0C,CAAC;AAEzE,+CAA+C;AAC/C,MAAM,MAAM,WAAW,GACpB,SAAS,GACT,QAAQ,GACR,SAAS,GACT,iBAAiB,GACjB,kBAAkB,GAClB,kBAAkB,GAClB,UAAU,CAAC;AAEd,qCAAqC;AACrC,MAAM,WAAW,aAAa;IAC7B,gCAAgC;IAChC,OAAO,EAAE,MAAM,CAAC;IAChB,uBAAuB;IACvB,KAAK,EAAE,WAAW,CAAC;IACnB,4BAA4B;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,gCAAgC;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,gDAAgD;IAChD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,gDAAgD;IAChD,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,2CAA2C;IAC3C,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,wBAAwB;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,kDAAkD;AAClD,MAAM,WAAW,kBAAkB;IAClC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,OAAO,CAAC,QAAQ,CAAC,CAAC;IAC1B,iDAAiD;IACjD,QAAQ,EAAE,OAAO,CAAC;IAClB,wCAAwC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+CAA+C;IAC/C,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,4BAA4B;IAC5B,OAAO,EAAE,MAAM,CAAC;CAChB;AAcD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAMjD;AAsBD;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,GAAG,EAAE,MAAM,GAAG,kBAAkB,CAwEjI"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Where a Request is right now: who holds it, doing what, since when.
|
|
3
|
+
*
|
|
4
|
+
* On 2026-10-14 an owner request went Ella → Pia → Dex, Dex finished, and
|
|
5
|
+
* the result sat in Dex's terminal for seven hours. Reconstructing that took
|
|
6
|
+
* reading three agents' transcripts. Everything needed was already recorded:
|
|
7
|
+
* the Request's open WorkItems (holder = `target`, time = `statusChangedAt`)
|
|
8
|
+
* and the agent ask ledger (a holder that handed the work to another agent
|
|
9
|
+
* with a plain message). This module joins them into one answer.
|
|
10
|
+
*
|
|
11
|
+
* Pure: the caller passes the Request, its WorkItems and the open asks.
|
|
12
|
+
*
|
|
13
|
+
* @module services/v3/request-whereabouts.service
|
|
14
|
+
*/
|
|
15
|
+
import { WORK_ITEM_REVIEWER_KEY } from '../../types/v2/work-item.types.js';
|
|
16
|
+
/** WorkItem statuses that still hold a request. */
|
|
17
|
+
const OPEN_STATUSES = new Set(['queued', 'scheduled', 'proposed', 'accepted', 'running', 'blocked', 'escalated', 'done_by_worker']);
|
|
18
|
+
/** Request statuses that wait on the owner. */
|
|
19
|
+
const OWNER_WAIT_STATUSES = new Set(['waiting_confirmation', 'awaiting_followup']);
|
|
20
|
+
/** Most hops followed along agent-to-agent asks. */
|
|
21
|
+
const MAX_ASK_HOPS = 4;
|
|
22
|
+
/** Request statuses that are final. */
|
|
23
|
+
const FINISHED_STATUSES = new Set(['done', 'cancelled']);
|
|
24
|
+
/**
|
|
25
|
+
* Format a duration for a person: "45m", "2h 10m", "3d 4h".
|
|
26
|
+
*
|
|
27
|
+
* @param ms - Duration
|
|
28
|
+
* @returns Short text
|
|
29
|
+
*/
|
|
30
|
+
export function formatDuration(ms) {
|
|
31
|
+
const m = Math.max(0, Math.floor(ms / 60_000));
|
|
32
|
+
if (m < 60)
|
|
33
|
+
return `${m}m`;
|
|
34
|
+
const h = Math.floor(m / 60);
|
|
35
|
+
if (h < 48)
|
|
36
|
+
return `${h}h ${m % 60}m`;
|
|
37
|
+
return `${Math.floor(h / 24)}d ${h % 24}h`;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* The state and holder a WorkItem implies.
|
|
41
|
+
*
|
|
42
|
+
* @param wi - An open WorkItem
|
|
43
|
+
* @param reviewItems - Review WorkItems by the id they verify
|
|
44
|
+
* @returns Holder session and state
|
|
45
|
+
*/
|
|
46
|
+
function holderOf(wi, reviewItems) {
|
|
47
|
+
if (wi.status === 'done_by_worker') {
|
|
48
|
+
const reviewer = (typeof wi.metadata?.[WORK_ITEM_REVIEWER_KEY] === 'string' ? wi.metadata[WORK_ITEM_REVIEWER_KEY] : undefined)
|
|
49
|
+
?? reviewItems.get(wi.id)?.target
|
|
50
|
+
?? 'unassigned';
|
|
51
|
+
return { session: reviewer, state: 'awaiting_review' };
|
|
52
|
+
}
|
|
53
|
+
const session = wi.target ?? 'unassigned';
|
|
54
|
+
if (wi.status === 'running')
|
|
55
|
+
return { session, state: 'working' };
|
|
56
|
+
if (wi.status === 'blocked' || wi.status === 'escalated')
|
|
57
|
+
return { session, state: 'blocked' };
|
|
58
|
+
return { session, state: 'queued' };
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* Compute where a request is now.
|
|
62
|
+
*
|
|
63
|
+
* Rules:
|
|
64
|
+
* - finished request → no holders;
|
|
65
|
+
* - each open WorkItem of the request (by `requestId` or `workItemIds`) is a
|
|
66
|
+
* holder: its `target` (running/queued/blocked) or, once the worker is done,
|
|
67
|
+
* its reviewer (awaiting_review). Review items themselves are folded in;
|
|
68
|
+
* - a holder that handed the work to another agent with a plain message (an
|
|
69
|
+
* open ask newer than the holder's own start) is `waiting_on_agent`, and
|
|
70
|
+
* the asked agent is a holder too (followed up to {@link MAX_ASK_HOPS});
|
|
71
|
+
* - no open WorkItem: the request's `assignee`/`ownerAgent` handles it, or the
|
|
72
|
+
* owner when the request waits on a confirmation or follow-up.
|
|
73
|
+
*
|
|
74
|
+
* @param request - The Request
|
|
75
|
+
* @param workItems - WorkItems to search (the pool, or a subset)
|
|
76
|
+
* @param openAsks - Open agent-to-agent asks (from the ledger)
|
|
77
|
+
* @param now - Current time (epoch ms)
|
|
78
|
+
* @returns The whereabouts
|
|
79
|
+
*
|
|
80
|
+
* @example
|
|
81
|
+
* ```typescript
|
|
82
|
+
* const w = computeWhereabouts(request, await pool.getAllItems(), ledger.openAsks(), Date.now());
|
|
83
|
+
* w.summary; // "With Dex (working on "Replicate the video") for 2h 10m"
|
|
84
|
+
* ```
|
|
85
|
+
*/
|
|
86
|
+
export function computeWhereabouts(request, workItems, openAsks, now) {
|
|
87
|
+
const base = {
|
|
88
|
+
requestId: request.id,
|
|
89
|
+
...(request.ticketNumber ? { ticketNumber: request.ticketNumber } : {}),
|
|
90
|
+
title: request.title,
|
|
91
|
+
status: request.status,
|
|
92
|
+
};
|
|
93
|
+
if (FINISHED_STATUSES.has(request.status)) {
|
|
94
|
+
const at = request.completedAt ?? request.updatedAt;
|
|
95
|
+
return { ...base, finished: true, finishedAt: at, holders: [], summary: `${request.status === 'done' ? 'Done' : 'Cancelled'}${at ? ` at ${at}` : ''}.` };
|
|
96
|
+
}
|
|
97
|
+
const linked = new Set(request.workItemIds ?? []);
|
|
98
|
+
const mine = workItems.filter((wi) => wi.requestId === request.id || linked.has(wi.id));
|
|
99
|
+
const reviewItems = new Map();
|
|
100
|
+
for (const wi of mine) {
|
|
101
|
+
const of = wi.metadata?.['verifyOf'];
|
|
102
|
+
if (wi.type === 'review' && typeof of === 'string')
|
|
103
|
+
reviewItems.set(of, wi);
|
|
104
|
+
}
|
|
105
|
+
const holders = [];
|
|
106
|
+
const seen = new Set();
|
|
107
|
+
const add = (h) => {
|
|
108
|
+
const key = `${h.session}|${h.workItemId ?? ''}|${h.state}`;
|
|
109
|
+
if (seen.has(key))
|
|
110
|
+
return;
|
|
111
|
+
seen.add(key);
|
|
112
|
+
holders.push({ ...h, forMs: Math.max(0, now - Date.parse(h.since)) });
|
|
113
|
+
};
|
|
114
|
+
for (const wi of mine) {
|
|
115
|
+
if (!OPEN_STATUSES.has(wi.status))
|
|
116
|
+
continue;
|
|
117
|
+
// A review item is shown through the item it reviews.
|
|
118
|
+
if (wi.type === 'review' && typeof wi.metadata?.['verifyOf'] === 'string' && mine.some((m) => m.id === wi.metadata?.['verifyOf']))
|
|
119
|
+
continue;
|
|
120
|
+
const { session, state } = holderOf(wi, reviewItems);
|
|
121
|
+
add({
|
|
122
|
+
session,
|
|
123
|
+
state,
|
|
124
|
+
since: wi.statusChangedAt ?? wi.startedAt ?? wi.createdAt,
|
|
125
|
+
workItemId: wi.id,
|
|
126
|
+
what: wi.title,
|
|
127
|
+
...(wi.blockedReason ? { blockedReason: wi.blockedReason } : {}),
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
if (holders.length === 0) {
|
|
131
|
+
if (OWNER_WAIT_STATUSES.has(request.status)) {
|
|
132
|
+
add({ session: 'owner', state: 'waiting_on_owner', since: request.updatedAt, what: request.title });
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
const agent = request.assignee ?? request.ownerAgent;
|
|
136
|
+
if (agent)
|
|
137
|
+
add({ session: agent, state: 'handling', since: request.updatedAt, what: request.title });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
// Follow plain-message hand-offs: a holder that asked another agent after
|
|
141
|
+
// it took the work is waiting on that agent.
|
|
142
|
+
let frontier = holders.filter((h) => h.session !== 'owner').map((h) => ({ session: h.session, sinceMs: Date.parse(h.since) }));
|
|
143
|
+
for (let hop = 0; hop < MAX_ASK_HOPS && frontier.length > 0; hop++) {
|
|
144
|
+
const next = [];
|
|
145
|
+
for (const f of frontier) {
|
|
146
|
+
const asks = openAsks.filter((a) => a.sender === f.session && a.deliveredAt >= f.sinceMs);
|
|
147
|
+
if (asks.length === 0)
|
|
148
|
+
continue;
|
|
149
|
+
for (const h of holders)
|
|
150
|
+
if (h.session === f.session && h.state !== 'awaiting_review')
|
|
151
|
+
h.state = 'waiting_on_agent';
|
|
152
|
+
for (const a of asks) {
|
|
153
|
+
const since = new Date(a.deliveredAt).toISOString();
|
|
154
|
+
add({ session: a.target, state: 'working', since, what: a.excerpt, from: a.sender });
|
|
155
|
+
next.push({ session: a.target, sinceMs: a.deliveredAt });
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
frontier = next;
|
|
159
|
+
}
|
|
160
|
+
holders.sort((a, b) => b.forMs - a.forMs);
|
|
161
|
+
return { ...base, finished: false, holders, summary: summarize(holders) };
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* One line for a person: where the request is, longest-held first.
|
|
165
|
+
*
|
|
166
|
+
* @param holders - Holders, sorted
|
|
167
|
+
* @returns Summary text
|
|
168
|
+
*/
|
|
169
|
+
function summarize(holders) {
|
|
170
|
+
const active = holders.filter((h) => h.state !== 'waiting_on_agent');
|
|
171
|
+
if (active.length === 0) {
|
|
172
|
+
return holders.length === 0 ? 'Nobody holds it: no open work item, no assignee.' : 'Handed on, but nobody is working on it.';
|
|
173
|
+
}
|
|
174
|
+
const phrase = {
|
|
175
|
+
working: 'working on',
|
|
176
|
+
queued: 'queued:',
|
|
177
|
+
blocked: 'blocked on',
|
|
178
|
+
awaiting_review: 'reviewing',
|
|
179
|
+
waiting_on_agent: 'waiting on another agent for',
|
|
180
|
+
waiting_on_owner: 'waiting on you for',
|
|
181
|
+
handling: 'handling',
|
|
182
|
+
};
|
|
183
|
+
return active
|
|
184
|
+
.slice(0, 3)
|
|
185
|
+
.map((h) => {
|
|
186
|
+
const who = h.session === 'owner' ? 'You' : h.session;
|
|
187
|
+
const what = h.what ? ` "${h.what.slice(0, 80)}"` : '';
|
|
188
|
+
const via = h.from ? `, from ${h.from}` : '';
|
|
189
|
+
return `${who} (${phrase[h.state]}${what}${via}) for ${formatDuration(h.forMs)}`;
|
|
190
|
+
})
|
|
191
|
+
.join('; ') + (active.length > 3 ? `; and ${active.length - 3} more` : '') + '.';
|
|
192
|
+
}
|
|
193
|
+
//# sourceMappingURL=request-whereabouts.service.js.map
|