crewly 1.20.286 → 1.20.288
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/search-chat/execute.sh +18 -0
- package/config/skills/agent/core/search-chat/execute.test.sh +5 -0
- package/dist/backend/backend/src/constants.d.ts +2 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +2 -0
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +12 -0
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts +10 -0
- package/dist/backend/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/agent-registration.service.js +62 -3
- package/dist/backend/backend/src/services/agent/agent-registration.service.js.map +1 -1
- package/dist/backend/backend/src/services/agent/resource-mode-protection.d.ts +39 -0
- package/dist/backend/backend/src/services/agent/resource-mode-protection.d.ts.map +1 -0
- package/dist/backend/backend/src/services/agent/resource-mode-protection.js +54 -0
- package/dist/backend/backend/src/services/agent/resource-mode-protection.js.map +1 -0
- package/dist/backend/backend/src/services/agent/resource-mode.service.d.ts +32 -3
- package/dist/backend/backend/src/services/agent/resource-mode.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/resource-mode.service.js +95 -9
- package/dist/backend/backend/src/services/agent/resource-mode.service.js.map +1 -1
- package/dist/backend/backend/src/services/agent/runtime-session-recovery.d.ts +21 -1
- package/dist/backend/backend/src/services/agent/runtime-session-recovery.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/runtime-session-recovery.js +55 -8
- package/dist/backend/backend/src/services/agent/runtime-session-recovery.js.map +1 -1
- package/dist/backend/backend/src/services/open-items/open-item-gate.d.ts +40 -0
- package/dist/backend/backend/src/services/open-items/open-item-gate.d.ts.map +1 -0
- package/dist/backend/backend/src/services/open-items/open-item-gate.js +61 -0
- package/dist/backend/backend/src/services/open-items/open-item-gate.js.map +1 -0
- package/dist/backend/backend/src/services/open-items/open-items.service.d.ts +48 -0
- package/dist/backend/backend/src/services/open-items/open-items.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/open-items/open-items.service.js +182 -17
- package/dist/backend/backend/src/services/open-items/open-items.service.js.map +1 -1
- package/dist/backend/backend/src/services/orc/reply-destination.wiring.d.ts +6 -0
- package/dist/backend/backend/src/services/orc/reply-destination.wiring.d.ts.map +1 -1
- package/dist/backend/backend/src/services/orc/reply-destination.wiring.js +80 -2
- package/dist/backend/backend/src/services/orc/reply-destination.wiring.js.map +1 -1
- package/dist/backend/backend/src/services/reconciler/reconcile-rules.d.ts +2 -1
- package/dist/backend/backend/src/services/reconciler/reconcile-rules.d.ts.map +1 -1
- package/dist/backend/backend/src/services/reconciler/reconcile-rules.js +9 -1
- package/dist/backend/backend/src/services/reconciler/reconcile-rules.js.map +1 -1
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.d.ts.map +1 -1
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js +11 -0
- package/dist/backend/backend/src/services/reconciler/reconciler-data-provider.js.map +1 -1
- package/dist/backend/backend/src/services/reconciler/reconciler.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/reconciler/reconciler.service.js +2 -2
- package/dist/backend/backend/src/services/reconciler/reconciler.service.js.map +1 -1
- package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts +21 -0
- package/dist/backend/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/task-pool/task-pool.service.js +50 -0
- package/dist/backend/backend/src/services/task-pool/task-pool.service.js.map +1 -1
- package/dist/backend/backend/src/services/trace/trace-metrics.d.ts.map +1 -1
- package/dist/backend/backend/src/services/trace/trace-metrics.js +4 -2
- package/dist/backend/backend/src/services/trace/trace-metrics.js.map +1 -1
- package/dist/backend/backend/src/services/trace/trace-recorder.d.ts +8 -0
- package/dist/backend/backend/src/services/trace/trace-recorder.d.ts.map +1 -1
- package/dist/backend/backend/src/services/trace/trace-recorder.js +12 -0
- package/dist/backend/backend/src/services/trace/trace-recorder.js.map +1 -1
- package/dist/backend/backend/src/services/v3/request.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/request.service.js +11 -1
- package/dist/backend/backend/src/services/v3/request.service.js.map +1 -1
- package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.js +6 -1
- package/dist/backend/backend/src/services/v3/workitem-dispatch.subscriber.js.map +1 -1
- package/dist/backend/backend/src/types/v2/open-item.types.d.ts +2 -0
- package/dist/backend/backend/src/types/v2/open-item.types.d.ts.map +1 -1
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +2 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +2 -0
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts +10 -0
- package/dist/cli/backend/src/services/agent/agent-registration.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/agent/agent-registration.service.js +62 -3
- package/dist/cli/backend/src/services/agent/agent-registration.service.js.map +1 -1
- package/dist/cli/backend/src/services/agent/resource-mode.service.d.ts +32 -3
- package/dist/cli/backend/src/services/agent/resource-mode.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/agent/resource-mode.service.js +95 -9
- package/dist/cli/backend/src/services/agent/resource-mode.service.js.map +1 -1
- package/dist/cli/backend/src/services/agent/runtime-session-recovery.d.ts +21 -1
- package/dist/cli/backend/src/services/agent/runtime-session-recovery.d.ts.map +1 -1
- package/dist/cli/backend/src/services/agent/runtime-session-recovery.js +55 -8
- package/dist/cli/backend/src/services/agent/runtime-session-recovery.js.map +1 -1
- package/dist/cli/backend/src/services/open-items/open-item-gate.d.ts +40 -0
- package/dist/cli/backend/src/services/open-items/open-item-gate.d.ts.map +1 -0
- package/dist/cli/backend/src/services/open-items/open-item-gate.js +61 -0
- package/dist/cli/backend/src/services/open-items/open-item-gate.js.map +1 -0
- package/dist/cli/backend/src/services/open-items/open-items.service.d.ts +48 -0
- package/dist/cli/backend/src/services/open-items/open-items.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/open-items/open-items.service.js +182 -17
- package/dist/cli/backend/src/services/open-items/open-items.service.js.map +1 -1
- package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts +21 -0
- package/dist/cli/backend/src/services/task-pool/task-pool.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/task-pool/task-pool.service.js +50 -0
- package/dist/cli/backend/src/services/task-pool/task-pool.service.js.map +1 -1
- package/dist/cli/backend/src/services/trace/trace-recorder.d.ts +8 -0
- package/dist/cli/backend/src/services/trace/trace-recorder.d.ts.map +1 -1
- package/dist/cli/backend/src/services/trace/trace-recorder.js +12 -0
- package/dist/cli/backend/src/services/trace/trace-recorder.js.map +1 -1
- package/dist/cli/backend/src/services/v3/request.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/v3/request.service.js +11 -1
- package/dist/cli/backend/src/services/v3/request.service.js.map +1 -1
- package/dist/cli/backend/src/types/v2/open-item.types.d.ts +2 -0
- package/dist/cli/backend/src/types/v2/open-item.types.d.ts.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The gate of a conditional promise (a commitment waiting on the owner): the
|
|
3
|
+
* question from the same message ("你看这样写行吗?你点头后我就…") whose card
|
|
4
|
+
* answer opens or drops it (CREW-440). Pure helpers, no I/O.
|
|
5
|
+
*
|
|
6
|
+
* @module services/open-items/open-item-gate
|
|
7
|
+
*/
|
|
8
|
+
import type { OwnerDecision } from '../../types/decision.types.js';
|
|
9
|
+
import type { RequestOpenItem } from '../../types/v2/open-item.types.js';
|
|
10
|
+
/** What a settled (or pending) gate decision means for the promise. */
|
|
11
|
+
export type GateVerdict = 'wait' | 'open' | 'skipped' | 'declined';
|
|
12
|
+
/**
|
|
13
|
+
* The question item a conditional promise waits behind: `gateItemId`, else
|
|
14
|
+
* (promises stored before CREW-440) the question of the same source message.
|
|
15
|
+
*
|
|
16
|
+
* @param item - The waiting commitment
|
|
17
|
+
* @param items - All items of the request
|
|
18
|
+
* @returns The question item, or null
|
|
19
|
+
*/
|
|
20
|
+
export declare function siblingQuestion(item: RequestOpenItem, items: readonly RequestOpenItem[]): RequestOpenItem | null;
|
|
21
|
+
/**
|
|
22
|
+
* What an owner's answer on the gate card means for the promise.
|
|
23
|
+
*
|
|
24
|
+
* @param d - The gate decision
|
|
25
|
+
* @returns `open` (yes), `skipped` (drop it, tell the agent), `declined` (no / withdrawn), `wait` (still pending, or "reply in words")
|
|
26
|
+
*/
|
|
27
|
+
export declare function gateVerdict(d: Pick<OwnerDecision, 'status' | 'chosenKey' | 'yesKey' | 'options'>): GateVerdict;
|
|
28
|
+
/**
|
|
29
|
+
* A waiting promise nobody can open: its sibling question was dropped (skipped,
|
|
30
|
+
* withdrawn, expired), or it has neither a sibling nor a gate card. A sibling
|
|
31
|
+
* that was ANSWERED is not dead: the sweep opens or drops the promise by that
|
|
32
|
+
* answer. (Auto-accept by silence closes dead ones instead of parking the
|
|
33
|
+
* ticket in `awaiting_followup`.)
|
|
34
|
+
*
|
|
35
|
+
* @param item - Item
|
|
36
|
+
* @param items - All items of the request
|
|
37
|
+
* @returns True when no answer can ever reach it
|
|
38
|
+
*/
|
|
39
|
+
export declare function isDeadGate(item: RequestOpenItem, items: readonly RequestOpenItem[]): boolean;
|
|
40
|
+
//# sourceMappingURL=open-item-gate.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open-item-gate.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/open-items/open-item-gate.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mCAAmC,CAAC;AAEzE,uEAAuE;AACvE,MAAM,MAAM,WAAW,GAAG,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,UAAU,CAAC;AAEnE;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,SAAS,eAAe,EAAE,GAAG,eAAe,GAAG,IAAI,CAGhH;AAED;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,IAAI,CAAC,aAAa,EAAE,QAAQ,GAAG,WAAW,GAAG,QAAQ,GAAG,SAAS,CAAC,GAAG,WAAW,CAQ9G;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,eAAe,EAAE,KAAK,EAAE,SAAS,eAAe,EAAE,GAAG,OAAO,CAK5F"}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The gate of a conditional promise (a commitment waiting on the owner): the
|
|
3
|
+
* question from the same message ("你看这样写行吗?你点头后我就…") whose card
|
|
4
|
+
* answer opens or drops it (CREW-440). Pure helpers, no I/O.
|
|
5
|
+
*
|
|
6
|
+
* @module services/open-items/open-item-gate
|
|
7
|
+
*/
|
|
8
|
+
import { OPEN_ITEMS_CONSTANTS } from '../../constants.js';
|
|
9
|
+
/**
|
|
10
|
+
* The question item a conditional promise waits behind: `gateItemId`, else
|
|
11
|
+
* (promises stored before CREW-440) the question of the same source message.
|
|
12
|
+
*
|
|
13
|
+
* @param item - The waiting commitment
|
|
14
|
+
* @param items - All items of the request
|
|
15
|
+
* @returns The question item, or null
|
|
16
|
+
*/
|
|
17
|
+
export function siblingQuestion(item, items) {
|
|
18
|
+
if (item.gateItemId)
|
|
19
|
+
return items.find((i) => i.id === item.gateItemId) ?? null;
|
|
20
|
+
return items.find((i) => i.type === 'question' && i.sourceMessageId === item.sourceMessageId) ?? null;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* What an owner's answer on the gate card means for the promise.
|
|
24
|
+
*
|
|
25
|
+
* @param d - The gate decision
|
|
26
|
+
* @returns `open` (yes), `skipped` (drop it, tell the agent), `declined` (no / withdrawn), `wait` (still pending, or "reply in words")
|
|
27
|
+
*/
|
|
28
|
+
export function gateVerdict(d) {
|
|
29
|
+
if (d.status === 'open' || d.status === 'parked')
|
|
30
|
+
return 'wait';
|
|
31
|
+
if (d.status === 'skipped')
|
|
32
|
+
return 'skipped';
|
|
33
|
+
if (d.status === 'cancelled' || d.status === 'expired')
|
|
34
|
+
return 'declined';
|
|
35
|
+
const chosen = d.chosenKey ? d.options?.find((o) => o.key === d.chosenKey) : undefined;
|
|
36
|
+
if (chosen?.label === OPEN_ITEMS_CONSTANTS.REPLY_LABEL)
|
|
37
|
+
return 'wait';
|
|
38
|
+
if (d.chosenKey && d.yesKey && d.chosenKey !== d.yesKey)
|
|
39
|
+
return 'declined';
|
|
40
|
+
return 'open';
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* A waiting promise nobody can open: its sibling question was dropped (skipped,
|
|
44
|
+
* withdrawn, expired), or it has neither a sibling nor a gate card. A sibling
|
|
45
|
+
* that was ANSWERED is not dead: the sweep opens or drops the promise by that
|
|
46
|
+
* answer. (Auto-accept by silence closes dead ones instead of parking the
|
|
47
|
+
* ticket in `awaiting_followup`.)
|
|
48
|
+
*
|
|
49
|
+
* @param item - Item
|
|
50
|
+
* @param items - All items of the request
|
|
51
|
+
* @returns True when no answer can ever reach it
|
|
52
|
+
*/
|
|
53
|
+
export function isDeadGate(item, items) {
|
|
54
|
+
if (item.type !== 'commitment' || item.status !== 'waiting_owner')
|
|
55
|
+
return false;
|
|
56
|
+
const sib = siblingQuestion(item, items);
|
|
57
|
+
if (!sib)
|
|
58
|
+
return !item.gateDecisionId;
|
|
59
|
+
return sib.status === 'skipped' || sib.status === 'superseded' || sib.status === 'expired' || sib.status === 'cancelled';
|
|
60
|
+
}
|
|
61
|
+
//# sourceMappingURL=open-item-gate.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"open-item-gate.js","sourceRoot":"","sources":["../../../../../../backend/src/services/open-items/open-item-gate.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAO1D;;;;;;;GAOG;AACH,MAAM,UAAU,eAAe,CAAC,IAAqB,EAAE,KAAiC;IACtF,IAAI,IAAI,CAAC,UAAU;QAAE,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,IAAI,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC;IAChF,OAAO,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,UAAU,IAAI,CAAC,CAAC,eAAe,KAAK,IAAI,CAAC,eAAe,CAAC,IAAI,IAAI,CAAC;AACxG,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,WAAW,CAAC,CAAqE;IAC/F,IAAI,CAAC,CAAC,MAAM,KAAK,MAAM,IAAI,CAAC,CAAC,MAAM,KAAK,QAAQ;QAAE,OAAO,MAAM,CAAC;IAChE,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,SAAS,CAAC;IAC7C,IAAI,CAAC,CAAC,MAAM,KAAK,WAAW,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS;QAAE,OAAO,UAAU,CAAC;IAC1E,MAAM,MAAM,GAAG,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;IACvF,IAAI,MAAM,EAAE,KAAK,KAAK,oBAAoB,CAAC,WAAW;QAAE,OAAO,MAAM,CAAC;IACtE,IAAI,CAAC,CAAC,SAAS,IAAI,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,SAAS,KAAK,CAAC,CAAC,MAAM;QAAE,OAAO,UAAU,CAAC;IAC3E,OAAO,MAAM,CAAC;AAChB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,UAAU,CAAC,IAAqB,EAAE,KAAiC;IACjF,IAAI,IAAI,CAAC,IAAI,KAAK,YAAY,IAAI,IAAI,CAAC,MAAM,KAAK,eAAe;QAAE,OAAO,KAAK,CAAC;IAChF,MAAM,GAAG,GAAG,eAAe,CAAC,IAAI,EAAE,KAAK,CAAC,CAAC;IACzC,IAAI,CAAC,GAAG;QAAE,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC;IACtC,OAAO,GAAG,CAAC,MAAM,KAAK,SAAS,IAAI,GAAG,CAAC,MAAM,KAAK,YAAY,IAAI,GAAG,CAAC,MAAM,KAAK,SAAS,IAAI,GAAG,CAAC,MAAM,KAAK,WAAW,CAAC;AAC3H,CAAC"}
|
|
@@ -524,6 +524,54 @@ export declare class OpenItemsService {
|
|
|
524
524
|
* @returns The note for the agent (null when withdrawn)
|
|
525
525
|
*/
|
|
526
526
|
onDecisionSettled(d: OwnerDecision, fallback: string | null | undefined): Promise<string | null>;
|
|
527
|
+
/**
|
|
528
|
+
* What would happen to a waiting promise now, without doing it.
|
|
529
|
+
*
|
|
530
|
+
* @param item - Commitment in `waiting_owner`
|
|
531
|
+
* @param items - All items of its request
|
|
532
|
+
* @param now - Clock
|
|
533
|
+
* @param hint - A decision already in hand (the one just settled)
|
|
534
|
+
* @returns The action, the item with its gate card linked, and when the owner answered
|
|
535
|
+
*/
|
|
536
|
+
private decideGate;
|
|
537
|
+
/**
|
|
538
|
+
* Settle one waiting promise by its gate: open it on a yes (follow-up
|
|
539
|
+
* WorkItem, the reconciler starts a stopped agent), close it on a no / skip
|
|
540
|
+
* (a skip tells the agent once to drop it), tell the owner when no gate can
|
|
541
|
+
* ever answer it.
|
|
542
|
+
*
|
|
543
|
+
* @param request - Request
|
|
544
|
+
* @param item - Commitment in `waiting_owner`
|
|
545
|
+
* @param items - All items of the request
|
|
546
|
+
* @param now - Clock
|
|
547
|
+
* @param pool - Every WorkItem
|
|
548
|
+
* @param hint - A decision already in hand
|
|
549
|
+
* @returns The item after (the same object when nothing changed)
|
|
550
|
+
*/
|
|
551
|
+
private runGate;
|
|
552
|
+
/**
|
|
553
|
+
* One pass over every request holding a waiting promise (the CREW-440
|
|
554
|
+
* backfill: promises stored before they carried a gate, e.g. TKT-374
|
|
555
|
+
* c-e21ac6f0-1, are settled by the card the owner already answered). The
|
|
556
|
+
* regular sweep does the same every minute; this adds a dry-run view and a
|
|
557
|
+
* one-time pass at startup.
|
|
558
|
+
*
|
|
559
|
+
* @param opts - `apply: true` to make the changes (default: dry-run)
|
|
560
|
+
* @returns How many requests / promises it examined, and what each would get
|
|
561
|
+
*/
|
|
562
|
+
backfillWaitingPromises(opts?: {
|
|
563
|
+
apply?: boolean;
|
|
564
|
+
}): Promise<{
|
|
565
|
+
dryRun: boolean;
|
|
566
|
+
examinedRequests: number;
|
|
567
|
+
examinedItems: number;
|
|
568
|
+
rows: Array<{
|
|
569
|
+
requestId: string;
|
|
570
|
+
ticket: string;
|
|
571
|
+
item: string;
|
|
572
|
+
action: string;
|
|
573
|
+
}>;
|
|
574
|
+
}>;
|
|
527
575
|
/**
|
|
528
576
|
* The owner skipped an open item ("I don't care about this anymore"). A
|
|
529
577
|
* promise is closed, its follow-up WorkItem cancelled and the agent told
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"open-items.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/open-items/open-items.service.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AAKH,OAAO,EAAiB,KAAK,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAChF,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iCAAiC,CAAC;AAC/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mCAAmC,CAAC;AAClE,OAAO,EAA6B,KAAK,eAAe,EAAE,MAAM,mCAAmC,CAAC;AACpG,OAAO,KAAK,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,+BAA+B,CAAC;AAGnF,OAAO,EAAoG,KAAK,mBAAmB,EAAE,MAAM,qBAAqB,CAAC;AAEjK,OAAO,EAA+B,KAAK,cAAc,EAAE,MAAM,0CAA0C,CAAC;
|
|
1
|
+
{"version":3,"file":"open-items.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/open-items/open-items.service.ts"],"names":[],"mappings":"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"}
|
|
@@ -43,6 +43,7 @@ import { AgentPromptReferenceService } from '../orc/agent-prompt-reference.servi
|
|
|
43
43
|
import { withQueueMeta } from '../messaging/queue-priority.js';
|
|
44
44
|
import { parseSlackThreadKey } from '../slack/slack-thread-key.js';
|
|
45
45
|
import { slackArchiveLink } from '../decisions/ticket-thread-store.js';
|
|
46
|
+
import { gateVerdict, siblingQuestion } from './open-item-gate.js';
|
|
46
47
|
/** Thrown for open-item requests the caller must fix (HTTP 4xx). */
|
|
47
48
|
export class OpenItemsError extends Error {
|
|
48
49
|
status;
|
|
@@ -481,6 +482,7 @@ export class OpenItemsService {
|
|
|
481
482
|
start() {
|
|
482
483
|
if (this.timer)
|
|
483
484
|
return;
|
|
485
|
+
void this.backfillWaitingPromises({ apply: true }).catch((err) => this.logger.warn('Waiting promises backfill failed', { error: errText(err) }));
|
|
484
486
|
this.timer = setInterval(() => {
|
|
485
487
|
void this.sweep().catch((err) => this.logger.warn('Open-items sweep failed', { error: errText(err) }));
|
|
486
488
|
}, OPEN_ITEMS_CONSTANTS.SWEEP_INTERVAL_MS);
|
|
@@ -614,6 +616,17 @@ export class OpenItemsService {
|
|
|
614
616
|
items.push(item);
|
|
615
617
|
changed = true;
|
|
616
618
|
}
|
|
619
|
+
// The card of a question now exists: it is the gate of the promise made in the same message.
|
|
620
|
+
const freshIds = new Set(fresh.map((p) => p.item.id));
|
|
621
|
+
for (let k = 0; k < items.length; k++) {
|
|
622
|
+
if (items[k].status !== 'waiting_owner' || !freshIds.has(items[k].id))
|
|
623
|
+
continue;
|
|
624
|
+
const next = await this.runGate(request, items[k], items, at, pool);
|
|
625
|
+
if (next !== items[k]) {
|
|
626
|
+
items[k] = next;
|
|
627
|
+
changed = true;
|
|
628
|
+
}
|
|
629
|
+
}
|
|
617
630
|
if (!changed)
|
|
618
631
|
return null;
|
|
619
632
|
return this.save(request, items, fresh.length > 0);
|
|
@@ -692,8 +705,21 @@ export class OpenItemsService {
|
|
|
692
705
|
for (const [i, c] of found.commitments.entries()) {
|
|
693
706
|
if (c.waitsOnOwner) {
|
|
694
707
|
// Conditional on the owner: no due time, no follow-up, no nudges until they say yes.
|
|
695
|
-
|
|
696
|
-
|
|
708
|
+
// Same message asks a question ("你看这样写行吗?你点头后我就…"): the card the
|
|
709
|
+
// service posts for it is the gate. It does not exist yet, so link the item
|
|
710
|
+
// now and the card once it is posted (CREW-440). Otherwise: an earlier ask-owner card.
|
|
711
|
+
const sibling = found.questions.length > 0 ? base('question', 1).id : null;
|
|
712
|
+
const gate = sibling ? null : await this.recentDecision(message.senderId, at);
|
|
713
|
+
out.push({
|
|
714
|
+
item: {
|
|
715
|
+
...base('commitment', i + 1),
|
|
716
|
+
type: 'commitment',
|
|
717
|
+
text: c.text,
|
|
718
|
+
status: 'waiting_owner',
|
|
719
|
+
...(sibling ? { gateItemId: sibling } : {}),
|
|
720
|
+
...(gate ? { gateDecisionId: gate.id } : {}),
|
|
721
|
+
},
|
|
722
|
+
});
|
|
697
723
|
continue;
|
|
698
724
|
}
|
|
699
725
|
out.push({ item: { ...base('commitment', i + 1), type: 'commitment', text: c.text, due: c.due.toISOString(), dueSource: c.dueSource } });
|
|
@@ -1099,6 +1125,17 @@ export class OpenItemsService {
|
|
|
1099
1125
|
const items = (request.openItems ?? []).map((i) => i.id === ref.itemId && ACTIVE_OPEN_ITEM_STATUSES.has(i.status)
|
|
1100
1126
|
? { ...i, status, closedAt: now, closedReason: `${d.id} ${d.status}`, ...(answer ? { answer } : {}) }
|
|
1101
1127
|
: i);
|
|
1128
|
+
// The card also answers the promise stored beside the question (CREW-440):
|
|
1129
|
+
// yes opens it (follow-up WorkItem, the agent is woken), skip / no closes it.
|
|
1130
|
+
const pool = await this.deps.listWorkItems().catch(() => []);
|
|
1131
|
+
for (let k = 0; k < items.length; k++) {
|
|
1132
|
+
const i = items[k];
|
|
1133
|
+
if (i.type !== 'commitment' || i.status !== 'waiting_owner')
|
|
1134
|
+
continue;
|
|
1135
|
+
if (i.gateDecisionId !== d.id && siblingQuestion(i, items)?.id !== ref.itemId)
|
|
1136
|
+
continue;
|
|
1137
|
+
items[k] = await this.runGate(request, i, items, this.now(), pool, d);
|
|
1138
|
+
}
|
|
1102
1139
|
await this.save(request, items, false);
|
|
1103
1140
|
}).catch((err) => this.logger.warn('Answered question not recorded', { decisionId: d.id, error: errText(err) }));
|
|
1104
1141
|
if (d.status === 'cancelled')
|
|
@@ -1110,6 +1147,130 @@ export class OpenItemsService {
|
|
|
1110
1147
|
return fallback ?? null;
|
|
1111
1148
|
}
|
|
1112
1149
|
// -------------------------------------------------------------------------
|
|
1150
|
+
// Gates of conditional promises (CREW-440)
|
|
1151
|
+
// -------------------------------------------------------------------------
|
|
1152
|
+
/**
|
|
1153
|
+
* What would happen to a waiting promise now, without doing it.
|
|
1154
|
+
*
|
|
1155
|
+
* @param item - Commitment in `waiting_owner`
|
|
1156
|
+
* @param items - All items of its request
|
|
1157
|
+
* @param now - Clock
|
|
1158
|
+
* @param hint - A decision already in hand (the one just settled)
|
|
1159
|
+
* @returns The action, the item with its gate card linked, and when the owner answered
|
|
1160
|
+
*/
|
|
1161
|
+
async decideGate(item, items, now, hint) {
|
|
1162
|
+
const sib = siblingQuestion(item, items);
|
|
1163
|
+
const decisionId = item.gateDecisionId ?? sib?.decisionId;
|
|
1164
|
+
const linked = decisionId && decisionId !== item.gateDecisionId ? { ...item, gateDecisionId: decisionId } : item;
|
|
1165
|
+
if (decisionId) {
|
|
1166
|
+
const d = hint?.id === decisionId ? hint : await this.deps.getDecision?.(decisionId).catch(() => null);
|
|
1167
|
+
if (d)
|
|
1168
|
+
return { action: gateVerdict(d), item: linked, at: d.resolvedAt ? new Date(d.resolvedAt) : now, decisionId };
|
|
1169
|
+
}
|
|
1170
|
+
if (sib) {
|
|
1171
|
+
if (ACTIVE_OPEN_ITEM_STATUSES.has(sib.status))
|
|
1172
|
+
return { action: 'wait', item: linked, at: now };
|
|
1173
|
+
// The question is closed and its card cannot be read: the item's own status says how.
|
|
1174
|
+
if (sib.status === 'skipped')
|
|
1175
|
+
return { action: 'skipped', item: linked, at: now, ...(decisionId ? { decisionId } : {}) };
|
|
1176
|
+
if (sib.status === 'resolved') {
|
|
1177
|
+
const answer = sib.answer ?? '';
|
|
1178
|
+
const action = isApproval(answer) ? 'open' : answer === OPEN_ITEMS_CONSTANTS.NO_LABEL ? 'declined' : 'wait';
|
|
1179
|
+
return { action, item: linked, at: sib.closedAt ? new Date(sib.closedAt) : now, ...(decisionId ? { decisionId } : {}) };
|
|
1180
|
+
}
|
|
1181
|
+
return { action: 'declined', item: linked, at: now, ...(decisionId ? { decisionId } : {}) };
|
|
1182
|
+
}
|
|
1183
|
+
if (decisionId)
|
|
1184
|
+
return { action: 'wait', item: linked, at: now };
|
|
1185
|
+
const stuck = now.getTime() - Date.parse(item.createdAt) >= OPEN_ITEMS_CONSTANTS.GATE_SURFACE_AFTER_MS;
|
|
1186
|
+
return { action: stuck && !item.ownerNotifiedAt ? 'surface' : 'wait', item: linked, at: now };
|
|
1187
|
+
}
|
|
1188
|
+
/**
|
|
1189
|
+
* Settle one waiting promise by its gate: open it on a yes (follow-up
|
|
1190
|
+
* WorkItem, the reconciler starts a stopped agent), close it on a no / skip
|
|
1191
|
+
* (a skip tells the agent once to drop it), tell the owner when no gate can
|
|
1192
|
+
* ever answer it.
|
|
1193
|
+
*
|
|
1194
|
+
* @param request - Request
|
|
1195
|
+
* @param item - Commitment in `waiting_owner`
|
|
1196
|
+
* @param items - All items of the request
|
|
1197
|
+
* @param now - Clock
|
|
1198
|
+
* @param pool - Every WorkItem
|
|
1199
|
+
* @param hint - A decision already in hand
|
|
1200
|
+
* @returns The item after (the same object when nothing changed)
|
|
1201
|
+
*/
|
|
1202
|
+
async runGate(request, item, items, now, pool, hint) {
|
|
1203
|
+
const g = await this.decideGate(item, items, now, hint);
|
|
1204
|
+
const nowIso = now.toISOString();
|
|
1205
|
+
const why = g.decisionId ?? 'its question';
|
|
1206
|
+
switch (g.action) {
|
|
1207
|
+
case 'open': {
|
|
1208
|
+
const { due, source } = parseDue(g.item.text, g.at);
|
|
1209
|
+
this.logger.info('Conditional promise opened by the owner answer', { tkt: ticketLabel(request), item: item.id, gate: g.decisionId });
|
|
1210
|
+
return this.activate(request, { item: { ...g.item, status: 'open', due: due.toISOString(), dueSource: source } }, pool);
|
|
1211
|
+
}
|
|
1212
|
+
case 'skipped': {
|
|
1213
|
+
this.logger.info('Conditional promise dropped: the owner skipped its question', { tkt: ticketLabel(request), item: item.id, gate: g.decisionId });
|
|
1214
|
+
const closed = { ...g.item, status: 'skipped', closedAt: nowIso, closedReason: `${why} skipped: the owner dropped it` };
|
|
1215
|
+
await this.deps.deliverToAgent(item.agent, dropNote(request, item)).catch(() => false);
|
|
1216
|
+
return closed;
|
|
1217
|
+
}
|
|
1218
|
+
case 'declined':
|
|
1219
|
+
this.logger.info('Conditional promise closed: the owner did not agree', { tkt: ticketLabel(request), item: item.id, gate: g.decisionId });
|
|
1220
|
+
return { ...g.item, status: 'superseded', closedAt: nowIso, closedReason: `${why}: the owner did not agree` };
|
|
1221
|
+
case 'surface': {
|
|
1222
|
+
const name = (await this.deps.displayName?.(item.agent).catch(() => undefined)) ?? item.agent;
|
|
1223
|
+
const note = `${name} said "${short(item.text, 160)}" once you agree, but no question is waiting on you for it. Say yes in this thread to start it, or skip it.`;
|
|
1224
|
+
const ok = await this.deps.postOwnerNote(request, note).catch(() => false);
|
|
1225
|
+
this.logger.warn('Conditional promise has no gate — owner told in the thread', { tkt: ticketLabel(request), item: item.id, posted: ok });
|
|
1226
|
+
return ok ? { ...g.item, ownerNotifiedAt: nowIso } : g.item;
|
|
1227
|
+
}
|
|
1228
|
+
default:
|
|
1229
|
+
return g.item;
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
/**
|
|
1233
|
+
* One pass over every request holding a waiting promise (the CREW-440
|
|
1234
|
+
* backfill: promises stored before they carried a gate, e.g. TKT-374
|
|
1235
|
+
* c-e21ac6f0-1, are settled by the card the owner already answered). The
|
|
1236
|
+
* regular sweep does the same every minute; this adds a dry-run view and a
|
|
1237
|
+
* one-time pass at startup.
|
|
1238
|
+
*
|
|
1239
|
+
* @param opts - `apply: true` to make the changes (default: dry-run)
|
|
1240
|
+
* @returns How many requests / promises it examined, and what each would get
|
|
1241
|
+
*/
|
|
1242
|
+
async backfillWaitingPromises(opts = {}) {
|
|
1243
|
+
return this.serial(async () => {
|
|
1244
|
+
const now = this.now();
|
|
1245
|
+
const all = await this.deps.requests.listAll();
|
|
1246
|
+
const holding = all.filter((r) => r.status !== 'cancelled' && (r.openItems ?? []).some((i) => i.status === 'waiting_owner'));
|
|
1247
|
+
const pool = await this.deps.listWorkItems().catch(() => []);
|
|
1248
|
+
const report = { dryRun: opts.apply !== true, examinedRequests: holding.length, examinedItems: 0, rows: [] };
|
|
1249
|
+
for (const request of holding) {
|
|
1250
|
+
let items = [...(request.openItems ?? [])];
|
|
1251
|
+
let changed = false;
|
|
1252
|
+
for (let k = 0; k < items.length; k++) {
|
|
1253
|
+
if (items[k].status !== 'waiting_owner')
|
|
1254
|
+
continue;
|
|
1255
|
+
report.examinedItems += 1;
|
|
1256
|
+
const g = await this.decideGate(items[k], items, now);
|
|
1257
|
+
report.rows.push({ requestId: request.id, ticket: ticketLabel(request), item: items[k].id, action: g.action });
|
|
1258
|
+
if (opts.apply !== true || g.action === 'wait')
|
|
1259
|
+
continue;
|
|
1260
|
+
const next = await this.runGate(request, items[k], items, now, pool);
|
|
1261
|
+
if (next !== items[k]) {
|
|
1262
|
+
items[k] = next;
|
|
1263
|
+
changed = true;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
if (changed)
|
|
1267
|
+
items = (await this.save(request, items, false)).openItems ?? items;
|
|
1268
|
+
}
|
|
1269
|
+
this.logger.info('Waiting promises backfill', { apply: opts.apply === true, requests: report.examinedRequests, items: report.examinedItems });
|
|
1270
|
+
return report;
|
|
1271
|
+
});
|
|
1272
|
+
}
|
|
1273
|
+
// -------------------------------------------------------------------------
|
|
1113
1274
|
// Owner skips
|
|
1114
1275
|
// -------------------------------------------------------------------------
|
|
1115
1276
|
/**
|
|
@@ -1148,9 +1309,7 @@ export class OpenItemsService {
|
|
|
1148
1309
|
if (cur.type === 'commitment') {
|
|
1149
1310
|
if (cur.workItemId)
|
|
1150
1311
|
await this.closeFollowUp(cur.workItemId, 'cancelled', 'The owner skipped this promise');
|
|
1151
|
-
note =
|
|
1152
|
-
`[FOLLOW-UP ${ticketLabel(fresh)}] The owner skipped this — drop it, don't ask again. ` +
|
|
1153
|
-
`You had promised: "${short(cur.text, 200)}". Don't deliver it unless the owner asks again.`;
|
|
1312
|
+
note = dropNote(fresh, cur);
|
|
1154
1313
|
}
|
|
1155
1314
|
else {
|
|
1156
1315
|
note = `[FOLLOW-UP ${ticketLabel(fresh)}] The owner skipped this — drop it, don't ask again: "${short(cur.text, 200)}".`;
|
|
@@ -1309,19 +1468,14 @@ export class OpenItemsService {
|
|
|
1309
1468
|
return { ...item, status, closedAt: nowIso, closedReason: `${d.id} ${d.status}`, ...(answer ? { answer } : {}) };
|
|
1310
1469
|
}
|
|
1311
1470
|
if (item.status === 'waiting_owner') {
|
|
1312
|
-
// No due time, no nudges. A card answer opens it; a declined card closes it.
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
if (!d || d.status === 'open' || d.status === 'parked')
|
|
1317
|
-
return item;
|
|
1318
|
-
if (d.status === 'cancelled' || d.status === 'expired' || (d.chosenKey && d.yesKey && d.chosenKey !== d.yesKey)) {
|
|
1471
|
+
// No due time, no nudges. A card answer opens it; a declined or skipped card closes it.
|
|
1472
|
+
const before = item;
|
|
1473
|
+
const next = await this.runGate(request, item, request.openItems ?? [], now, pool);
|
|
1474
|
+
if (next.status !== before.status)
|
|
1319
1475
|
counts.closed += 1;
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
const { due, source } = parseDue(item.text, at);
|
|
1324
|
-
return this.activate(request, { item: { ...item, status: 'open', due: due.toISOString(), dueSource: source } }, pool);
|
|
1476
|
+
else if (next.ownerNotifiedAt && !before.ownerNotifiedAt)
|
|
1477
|
+
counts.ownerNotes += 1;
|
|
1478
|
+
return next;
|
|
1325
1479
|
}
|
|
1326
1480
|
// Commitment whose follow-up WorkItem was finished or cancelled (by anyone,
|
|
1327
1481
|
// through the task API): the item is closed with it, so it cannot nudge or be re-armed.
|
|
@@ -1407,6 +1561,17 @@ export class OpenItemsService {
|
|
|
1407
1561
|
await this.deps.closeFollowUp?.(workItemId, outcome, reason).catch((err) => this.logger.debug('Follow-up WorkItem not closed', { workItemId, error: errText(err) }));
|
|
1408
1562
|
}
|
|
1409
1563
|
}
|
|
1564
|
+
/**
|
|
1565
|
+
* The note that tells an agent once to drop a promise the owner skipped.
|
|
1566
|
+
*
|
|
1567
|
+
* @param request - Its request
|
|
1568
|
+
* @param item - The promise
|
|
1569
|
+
* @returns Note text
|
|
1570
|
+
*/
|
|
1571
|
+
function dropNote(request, item) {
|
|
1572
|
+
return (`[FOLLOW-UP ${ticketLabel(request)}] The owner skipped this — drop it, don't ask again. ` +
|
|
1573
|
+
`You had promised: "${short(item.text, 200)}". Don't deliver it unless the owner asks again.`);
|
|
1574
|
+
}
|
|
1410
1575
|
/**
|
|
1411
1576
|
* The open-item status a settled decision leaves its question in.
|
|
1412
1577
|
*
|