crewly 1.20.289 → 1.20.290

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.
Files changed (74) hide show
  1. package/dist/backend/backend/src/controllers/chat/agent-reply.controller.d.ts.map +1 -1
  2. package/dist/backend/backend/src/controllers/chat/agent-reply.controller.js +5 -0
  3. package/dist/backend/backend/src/controllers/chat/agent-reply.controller.js.map +1 -1
  4. package/dist/backend/backend/src/index.d.ts.map +1 -1
  5. package/dist/backend/backend/src/index.js +18 -12
  6. package/dist/backend/backend/src/index.js.map +1 -1
  7. package/dist/backend/backend/src/services/agent/active-work-briefing.service.d.ts +11 -0
  8. package/dist/backend/backend/src/services/agent/active-work-briefing.service.d.ts.map +1 -1
  9. package/dist/backend/backend/src/services/agent/active-work-briefing.service.js +32 -11
  10. package/dist/backend/backend/src/services/agent/active-work-briefing.service.js.map +1 -1
  11. package/dist/backend/backend/src/services/agent/follow-through.service.d.ts +132 -0
  12. package/dist/backend/backend/src/services/agent/follow-through.service.d.ts.map +1 -0
  13. package/dist/backend/backend/src/services/agent/follow-through.service.js +199 -0
  14. package/dist/backend/backend/src/services/agent/follow-through.service.js.map +1 -0
  15. package/dist/backend/backend/src/services/agent/follow-through.wiring.d.ts +62 -0
  16. package/dist/backend/backend/src/services/agent/follow-through.wiring.d.ts.map +1 -0
  17. package/dist/backend/backend/src/services/agent/follow-through.wiring.js +158 -0
  18. package/dist/backend/backend/src/services/agent/follow-through.wiring.js.map +1 -0
  19. package/dist/backend/backend/src/services/agent/idle-detection.service.d.ts +24 -6
  20. package/dist/backend/backend/src/services/agent/idle-detection.service.d.ts.map +1 -1
  21. package/dist/backend/backend/src/services/agent/idle-detection.service.js +46 -11
  22. package/dist/backend/backend/src/services/agent/idle-detection.service.js.map +1 -1
  23. package/dist/backend/backend/src/services/agent/idle-work-guard.d.ts +61 -0
  24. package/dist/backend/backend/src/services/agent/idle-work-guard.d.ts.map +1 -0
  25. package/dist/backend/backend/src/services/agent/idle-work-guard.js +81 -0
  26. package/dist/backend/backend/src/services/agent/idle-work-guard.js.map +1 -0
  27. package/dist/backend/backend/src/services/agent/stated-intent.d.ts +55 -0
  28. package/dist/backend/backend/src/services/agent/stated-intent.d.ts.map +1 -0
  29. package/dist/backend/backend/src/services/agent/stated-intent.js +142 -0
  30. package/dist/backend/backend/src/services/agent/stated-intent.js.map +1 -0
  31. package/dist/backend/backend/src/services/messaging/owner-thread-sentinel.service.d.ts +10 -0
  32. package/dist/backend/backend/src/services/messaging/owner-thread-sentinel.service.d.ts.map +1 -1
  33. package/dist/backend/backend/src/services/messaging/owner-thread-sentinel.service.js +25 -0
  34. package/dist/backend/backend/src/services/messaging/owner-thread-sentinel.service.js.map +1 -1
  35. package/dist/backend/backend/src/services/monitoring/agent-turn-state.d.ts +16 -0
  36. package/dist/backend/backend/src/services/monitoring/agent-turn-state.d.ts.map +1 -1
  37. package/dist/backend/backend/src/services/monitoring/agent-turn-state.js +31 -0
  38. package/dist/backend/backend/src/services/monitoring/agent-turn-state.js.map +1 -1
  39. package/dist/backend/backend/src/services/v3/ticket-review.service.d.ts.map +1 -1
  40. package/dist/backend/backend/src/services/v3/ticket-review.service.js +5 -0
  41. package/dist/backend/backend/src/services/v3/ticket-review.service.js.map +1 -1
  42. package/dist/backend/backend/src/services/v3/ticket-work-guard.d.ts +81 -0
  43. package/dist/backend/backend/src/services/v3/ticket-work-guard.d.ts.map +1 -0
  44. package/dist/backend/backend/src/services/v3/ticket-work-guard.js +122 -0
  45. package/dist/backend/backend/src/services/v3/ticket-work-guard.js.map +1 -0
  46. package/dist/backend/build-info.json +2 -2
  47. package/dist/cli/backend/src/services/agent/active-work-briefing.service.d.ts +11 -0
  48. package/dist/cli/backend/src/services/agent/active-work-briefing.service.d.ts.map +1 -1
  49. package/dist/cli/backend/src/services/agent/active-work-briefing.service.js +32 -11
  50. package/dist/cli/backend/src/services/agent/active-work-briefing.service.js.map +1 -1
  51. package/dist/cli/backend/src/services/agent/follow-through.service.d.ts +132 -0
  52. package/dist/cli/backend/src/services/agent/follow-through.service.d.ts.map +1 -0
  53. package/dist/cli/backend/src/services/agent/follow-through.service.js +199 -0
  54. package/dist/cli/backend/src/services/agent/follow-through.service.js.map +1 -0
  55. package/dist/cli/backend/src/services/agent/stated-intent.d.ts +55 -0
  56. package/dist/cli/backend/src/services/agent/stated-intent.d.ts.map +1 -0
  57. package/dist/cli/backend/src/services/agent/stated-intent.js +142 -0
  58. package/dist/cli/backend/src/services/agent/stated-intent.js.map +1 -0
  59. package/dist/cli/backend/src/services/messaging/owner-thread-sentinel.service.d.ts +10 -0
  60. package/dist/cli/backend/src/services/messaging/owner-thread-sentinel.service.d.ts.map +1 -1
  61. package/dist/cli/backend/src/services/messaging/owner-thread-sentinel.service.js +25 -0
  62. package/dist/cli/backend/src/services/messaging/owner-thread-sentinel.service.js.map +1 -1
  63. package/dist/cli/backend/src/services/monitoring/agent-turn-state.d.ts +16 -0
  64. package/dist/cli/backend/src/services/monitoring/agent-turn-state.d.ts.map +1 -1
  65. package/dist/cli/backend/src/services/monitoring/agent-turn-state.js +31 -0
  66. package/dist/cli/backend/src/services/monitoring/agent-turn-state.js.map +1 -1
  67. package/dist/cli/backend/src/services/v3/ticket-review.service.d.ts.map +1 -1
  68. package/dist/cli/backend/src/services/v3/ticket-review.service.js +5 -0
  69. package/dist/cli/backend/src/services/v3/ticket-review.service.js.map +1 -1
  70. package/dist/cli/backend/src/services/v3/ticket-work-guard.d.ts +81 -0
  71. package/dist/cli/backend/src/services/v3/ticket-work-guard.d.ts.map +1 -0
  72. package/dist/cli/backend/src/services/v3/ticket-work-guard.js +122 -0
  73. package/dist/cli/backend/src/services/v3/ticket-work-guard.js.map +1 -0
  74. package/package.json +1 -1
@@ -0,0 +1,132 @@
1
+ /**
2
+ * Agent follow-through guard.
3
+ *
4
+ * An agent that tells the owner "I'm doing X now" and then ends its turn has
5
+ * broken a promise that nothing tracked (2026-10-10, Pia, three videos: two
6
+ * turns ended on "I haven't started yet — the next step is …" and she was
7
+ * idle-stopped 24 minutes later). This service remembers the last stated
8
+ * intent per agent and, at turn end, nudges the agent once when nothing
9
+ * followed it — no tool call, no WorkItem for the work, no hand-off.
10
+ *
11
+ * Cheap by construction: text heuristics ({@link detectStatedIntent}) plus
12
+ * the runtime's own tool hooks; no model call. Off switch:
13
+ * `CREWLY_FOLLOW_THROUGH=off`.
14
+ *
15
+ * @module services/agent/follow-through.service
16
+ * @see specs/2026-10-10-agent-follow-through.md
17
+ */
18
+ /** Tunables (ms). */
19
+ export declare const FOLLOW_THROUGH_CONSTANTS: {
20
+ /** A statement older than this is stale: the turn that ended now is not about it */
21
+ readonly MAX_AGE_MS: number;
22
+ /** A statement keeps its agent from being idle-stopped for this long */
23
+ readonly HOLD_MS: number;
24
+ /** Never nudge one agent more often than this, whatever it says next */
25
+ readonly NUDGE_COOLDOWN_MS: number;
26
+ /** Without tool hooks: PTY output for longer than this after the statement counts as work */
27
+ readonly OUTPUT_WORK_MS: 45000;
28
+ /** After a runtime restart the first tool call is the harness's own register-self */
29
+ readonly TOOLS_AFTER_RESTART: 2;
30
+ };
31
+ /** What the guard needs from the rest of the backend. */
32
+ export interface FollowThroughDeps {
33
+ /** Deliver text into the agent's conversation (wakes nothing; false when not delivered) */
34
+ nudgeAgent: (session: string, text: string) => Promise<boolean>;
35
+ /** Tool calls started after `since`; null when the runtime reports no hooks */
36
+ toolStartsSince: (session: string, since: number) => number | null;
37
+ /** Epoch ms the agent's runtime process started, when known */
38
+ runtimeStartedAt?: (session: string) => number | null;
39
+ /** Fallback without hooks: ms of PTY output after `since`; null when unknown */
40
+ outputSpanSince?: (session: string, since: number) => number | null;
41
+ /** The agent handed the work to someone or put it on the pool since `since` */
42
+ handedOff: (session: string, since: number) => Promise<boolean>;
43
+ /** The agent is waiting on an owner card / question */
44
+ waitingOnOwner?: (session: string) => Promise<boolean>;
45
+ /** Owner-stopped or team-paused agents are never nudged */
46
+ isHeldBack?: (session: string) => boolean;
47
+ now?: () => number;
48
+ }
49
+ /** What a turn end did. */
50
+ export type TurnEndOutcome = 'none' | 'stale' | 'worked' | 'handed_off' | 'waiting' | 'held' | 'cooldown' | 'nudged' | 'not_delivered';
51
+ /**
52
+ * @param sentence - What the agent said
53
+ * @returns The nudge text (harness text, English)
54
+ */
55
+ export declare function followThroughNudge(sentence: string): string;
56
+ /**
57
+ * Tracks the last stated intent per agent and nudges once when a turn ends
58
+ * without following it.
59
+ */
60
+ export declare class AgentFollowThroughService {
61
+ private readonly deps;
62
+ private readonly statements;
63
+ private readonly lastNudgeAt;
64
+ private readonly lastReplyAt;
65
+ private readonly logger;
66
+ /** @param deps - Injected behaviour */
67
+ constructor(deps: FollowThroughDeps);
68
+ private now;
69
+ /**
70
+ * An agent posted to the owner (Slack or chat). A post that states an intent
71
+ * is remembered; any other post supersedes the earlier statement.
72
+ *
73
+ * @param input - Agent, the post's text, whether it was an interim "working on it" mark
74
+ */
75
+ noteAgentPost(input: {
76
+ agent: string;
77
+ text: string;
78
+ interim?: boolean;
79
+ at?: number;
80
+ }): void;
81
+ /**
82
+ * When the agent last posted something that was not a stated intent (an
83
+ * answer, a result, a question). Ticket work creation reads it.
84
+ *
85
+ * @param agent - Session
86
+ * @returns Epoch ms, or undefined
87
+ */
88
+ lastRealReplyAt(agent: string): number | undefined;
89
+ /**
90
+ * Whether the agent has an unfulfilled stated intent (nudged or not) that
91
+ * is recent enough to keep it from being idle-stopped.
92
+ *
93
+ * @param agent - Session
94
+ * @returns True while one is open
95
+ */
96
+ holdsIntent(agent: string): boolean;
97
+ /**
98
+ * Take the open statement away (the caller turns it into a WorkItem).
99
+ *
100
+ * @param agent - Session
101
+ * @returns The sentence, or null
102
+ */
103
+ takeIntent(agent: string): {
104
+ sentence: string;
105
+ at: number;
106
+ } | null;
107
+ /**
108
+ * The agent's turn ended: nudge it once when it stated an intent that
109
+ * nothing followed. Never throws.
110
+ *
111
+ * @param agent - Session
112
+ * @returns What happened
113
+ */
114
+ onTurnEnd(agent: string): Promise<TurnEndOutcome>;
115
+ /** Whether the agent did anything after the statement. */
116
+ private followed;
117
+ }
118
+ /** @returns The wired guard, or null before boot (and when switched off) */
119
+ export declare function getFollowThrough(): AgentFollowThroughService | null;
120
+ /** @param service - The guard to expose (null clears) */
121
+ export declare function setFollowThrough(service: AgentFollowThroughService | null): void;
122
+ /**
123
+ * Report an agent's post to the owner, if the guard runs. Never throws.
124
+ *
125
+ * @param input - Agent, text, interim flag
126
+ */
127
+ export declare function reportAgentPostForFollowThrough(input: {
128
+ agent: string;
129
+ text: string;
130
+ interim?: boolean;
131
+ }): void;
132
+ //# sourceMappingURL=follow-through.service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"follow-through.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/agent/follow-through.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Agent follow-through guard.
3
+ *
4
+ * An agent that tells the owner "I'm doing X now" and then ends its turn has
5
+ * broken a promise that nothing tracked (2026-10-10, Pia, three videos: two
6
+ * turns ended on "I haven't started yet — the next step is …" and she was
7
+ * idle-stopped 24 minutes later). This service remembers the last stated
8
+ * intent per agent and, at turn end, nudges the agent once when nothing
9
+ * followed it — no tool call, no WorkItem for the work, no hand-off.
10
+ *
11
+ * Cheap by construction: text heuristics ({@link detectStatedIntent}) plus
12
+ * the runtime's own tool hooks; no model call. Off switch:
13
+ * `CREWLY_FOLLOW_THROUGH=off`.
14
+ *
15
+ * @module services/agent/follow-through.service
16
+ * @see specs/2026-10-10-agent-follow-through.md
17
+ */
18
+ import { ORCHESTRATOR_SESSION_NAME } from '../../constants.js';
19
+ import { LoggerService } from '../core/logger.service.js';
20
+ import { detectStatedIntent } from './stated-intent.js';
21
+ /** Tunables (ms). */
22
+ export const FOLLOW_THROUGH_CONSTANTS = {
23
+ /** A statement older than this is stale: the turn that ended now is not about it */
24
+ MAX_AGE_MS: 30 * 60_000,
25
+ /** A statement keeps its agent from being idle-stopped for this long */
26
+ HOLD_MS: 60 * 60_000,
27
+ /** Never nudge one agent more often than this, whatever it says next */
28
+ NUDGE_COOLDOWN_MS: 10 * 60_000,
29
+ /** Without tool hooks: PTY output for longer than this after the statement counts as work */
30
+ OUTPUT_WORK_MS: 45_000,
31
+ /** After a runtime restart the first tool call is the harness's own register-self */
32
+ TOOLS_AFTER_RESTART: 2,
33
+ };
34
+ /**
35
+ * @param sentence - What the agent said
36
+ * @returns The nudge text (harness text, English)
37
+ */
38
+ export function followThroughNudge(sentence) {
39
+ return (`[FOLLOW-THROUGH] You told the owner you would do this now: "${sentence}". ` +
40
+ 'Your turn ended without starting it. Start it now, in this turn; ' +
41
+ "don't reply again until there's a result or a real blocker.");
42
+ }
43
+ /**
44
+ * Tracks the last stated intent per agent and nudges once when a turn ends
45
+ * without following it.
46
+ */
47
+ export class AgentFollowThroughService {
48
+ deps;
49
+ statements = new Map();
50
+ lastNudgeAt = new Map();
51
+ lastReplyAt = new Map();
52
+ logger;
53
+ /** @param deps - Injected behaviour */
54
+ constructor(deps) {
55
+ this.deps = deps;
56
+ this.logger = LoggerService.getInstance().createComponentLogger('FollowThrough');
57
+ }
58
+ now() {
59
+ return this.deps.now ? this.deps.now() : Date.now();
60
+ }
61
+ /**
62
+ * An agent posted to the owner (Slack or chat). A post that states an intent
63
+ * is remembered; any other post supersedes the earlier statement.
64
+ *
65
+ * @param input - Agent, the post's text, whether it was an interim "working on it" mark
66
+ */
67
+ noteAgentPost(input) {
68
+ if (!input.agent || input.agent === ORCHESTRATOR_SESSION_NAME)
69
+ return;
70
+ const at = input.at ?? this.now();
71
+ const found = detectStatedIntent(input.text);
72
+ if (found) {
73
+ this.statements.set(input.agent, { at, sentence: found.sentence, nudged: false });
74
+ return;
75
+ }
76
+ this.statements.delete(input.agent);
77
+ if (!input.interim)
78
+ this.lastReplyAt.set(input.agent, at);
79
+ }
80
+ /**
81
+ * When the agent last posted something that was not a stated intent (an
82
+ * answer, a result, a question). Ticket work creation reads it.
83
+ *
84
+ * @param agent - Session
85
+ * @returns Epoch ms, or undefined
86
+ */
87
+ lastRealReplyAt(agent) {
88
+ return this.lastReplyAt.get(agent);
89
+ }
90
+ /**
91
+ * Whether the agent has an unfulfilled stated intent (nudged or not) that
92
+ * is recent enough to keep it from being idle-stopped.
93
+ *
94
+ * @param agent - Session
95
+ * @returns True while one is open
96
+ */
97
+ holdsIntent(agent) {
98
+ const s = this.statements.get(agent);
99
+ return !!s && this.now() - s.at <= FOLLOW_THROUGH_CONSTANTS.HOLD_MS;
100
+ }
101
+ /**
102
+ * Take the open statement away (the caller turns it into a WorkItem).
103
+ *
104
+ * @param agent - Session
105
+ * @returns The sentence, or null
106
+ */
107
+ takeIntent(agent) {
108
+ const s = this.statements.get(agent);
109
+ if (!s)
110
+ return null;
111
+ this.statements.delete(agent);
112
+ return { sentence: s.sentence, at: s.at };
113
+ }
114
+ /**
115
+ * The agent's turn ended: nudge it once when it stated an intent that
116
+ * nothing followed. Never throws.
117
+ *
118
+ * @param agent - Session
119
+ * @returns What happened
120
+ */
121
+ async onTurnEnd(agent) {
122
+ try {
123
+ const s = this.statements.get(agent);
124
+ if (!s || s.nudged)
125
+ return 'none';
126
+ const now = this.now();
127
+ if (now - s.at > FOLLOW_THROUGH_CONSTANTS.MAX_AGE_MS) {
128
+ this.statements.delete(agent);
129
+ return 'stale';
130
+ }
131
+ if (this.followed(agent, s)) {
132
+ this.statements.delete(agent);
133
+ return 'worked';
134
+ }
135
+ if (await this.deps.handedOff(agent, s.at - 60_000).catch(() => false)) {
136
+ this.statements.delete(agent);
137
+ return 'handed_off';
138
+ }
139
+ if (await this.deps.waitingOnOwner?.(agent).catch(() => false))
140
+ return 'waiting';
141
+ if (this.deps.isHeldBack?.(agent))
142
+ return 'held';
143
+ // One nudge per statement; one per agent per cooldown, whatever it says next.
144
+ s.nudged = true;
145
+ const last = this.lastNudgeAt.get(agent);
146
+ if (last !== undefined && now - last < FOLLOW_THROUGH_CONSTANTS.NUDGE_COOLDOWN_MS)
147
+ return 'cooldown';
148
+ const ok = await this.deps.nudgeAgent(agent, followThroughNudge(s.sentence)).catch(() => false);
149
+ this.logger.info('Stated intent without action — agent nudged', { agent, sentence: s.sentence, delivered: ok });
150
+ if (!ok)
151
+ return 'not_delivered';
152
+ this.lastNudgeAt.set(agent, now);
153
+ return 'nudged';
154
+ }
155
+ catch (err) {
156
+ this.logger.debug('Follow-through check failed (non-fatal)', { agent, error: err instanceof Error ? err.message : String(err) });
157
+ return 'none';
158
+ }
159
+ }
160
+ /** Whether the agent did anything after the statement. */
161
+ followed(agent, s) {
162
+ const tools = this.deps.toolStartsSince(agent, s.at);
163
+ if (tools !== null) {
164
+ const restarted = (this.deps.runtimeStartedAt?.(agent) ?? 0) > s.at;
165
+ return tools >= (restarted ? FOLLOW_THROUGH_CONSTANTS.TOOLS_AFTER_RESTART : 1);
166
+ }
167
+ const span = this.deps.outputSpanSince?.(agent, s.at);
168
+ // No hooks and no output reading: cannot tell, so do not nag.
169
+ if (span === null || span === undefined)
170
+ return true;
171
+ return span >= FOLLOW_THROUGH_CONSTANTS.OUTPUT_WORK_MS;
172
+ }
173
+ }
174
+ // ---------------------------------------------------------------------------
175
+ // Singleton
176
+ // ---------------------------------------------------------------------------
177
+ let instance = null;
178
+ /** @returns The wired guard, or null before boot (and when switched off) */
179
+ export function getFollowThrough() {
180
+ return instance;
181
+ }
182
+ /** @param service - The guard to expose (null clears) */
183
+ export function setFollowThrough(service) {
184
+ instance = service;
185
+ }
186
+ /**
187
+ * Report an agent's post to the owner, if the guard runs. Never throws.
188
+ *
189
+ * @param input - Agent, text, interim flag
190
+ */
191
+ export function reportAgentPostForFollowThrough(input) {
192
+ try {
193
+ instance?.noteAgentPost(input);
194
+ }
195
+ catch {
196
+ /* best-effort */
197
+ }
198
+ }
199
+ //# sourceMappingURL=follow-through.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"follow-through.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/agent/follow-through.service.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Stated intent and deliverable extraction (text heuristics, no LLM).
3
+ *
4
+ * `detectStatedIntent` recognises an agent telling the owner it is starting
5
+ * work NOW ("I'm breaking down the shot list now", "next step is X", 「我现在…」,
6
+ * 「做好发你」). It is narrower than the owner-thread sentinel's promise
7
+ * detector on purpose: a promise is any "I'll report back"; a stated intent is
8
+ * "I am doing this right now", which a turn that ends right after it has
9
+ * broken. See specs/2026-10-10-agent-follow-through.md.
10
+ *
11
+ * `extractDeliverables` reads an owner's request for the separate things it
12
+ * asks for (numbered items, 「三个视频」, "one … another …") so partial
13
+ * delivery can be shown ("1 of 3 delivered").
14
+ *
15
+ * @module services/agent/stated-intent
16
+ */
17
+ /** What an agent said it is starting. */
18
+ export interface StatedIntent {
19
+ /** The sentence that says it (clipped) */
20
+ sentence: string;
21
+ }
22
+ /**
23
+ * Whether an agent post says it is starting work now.
24
+ *
25
+ * Returns null for finished-work reports, for text that waits on someone
26
+ * else, and for a post that ends in a question (the agent is asking, not
27
+ * starting).
28
+ *
29
+ * @param text - The agent's post to the owner
30
+ * @returns The stated intent, or null
31
+ *
32
+ * @example
33
+ * ```typescript
34
+ * detectStatedIntent("I'm breaking down the reference shot list now, then building the crab version.");
35
+ * // { sentence: "I'm breaking down the reference shot list now, …" }
36
+ * detectStatedIntent('Done — the video is attached.'); // null
37
+ * ```
38
+ */
39
+ export declare function detectStatedIntent(text: string): StatedIntent | null;
40
+ /** Most checklist items kept for one request. */
41
+ export declare const MAX_DELIVERABLES = 8;
42
+ /**
43
+ * The separate things an owner's request asks for.
44
+ *
45
+ * Order of trust: explicit numbered/bulleted lists, inline "(1) … (2) …" /
46
+ * 「第一…第二…」 / "first … second …", "one … another …" / 「一个…另一个…」, then a
47
+ * bare count ("three videos" → three items). Returns [] when the request asks
48
+ * for one thing (or the shape is unclear): a checklist is only worth showing
49
+ * when partial delivery is possible.
50
+ *
51
+ * @param text - The owner's words
52
+ * @returns 2..{@link MAX_DELIVERABLES} items, or []
53
+ */
54
+ export declare function extractDeliverables(text: string): string[];
55
+ //# sourceMappingURL=stated-intent.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stated-intent.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/agent/stated-intent.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AAEH,yCAAyC;AACzC,MAAM,WAAW,YAAY;IAC3B,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;CAClB;AAoBD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,GAAG,YAAY,GAAG,IAAI,CAcpE;AAMD,iDAAiD;AACjD,eAAO,MAAM,gBAAgB,IAAI,CAAC;AAiBlC;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CA0C1D"}
@@ -0,0 +1,142 @@
1
+ /**
2
+ * Stated intent and deliverable extraction (text heuristics, no LLM).
3
+ *
4
+ * `detectStatedIntent` recognises an agent telling the owner it is starting
5
+ * work NOW ("I'm breaking down the shot list now", "next step is X", 「我现在…」,
6
+ * 「做好发你」). It is narrower than the owner-thread sentinel's promise
7
+ * detector on purpose: a promise is any "I'll report back"; a stated intent is
8
+ * "I am doing this right now", which a turn that ends right after it has
9
+ * broken. See specs/2026-10-10-agent-follow-through.md.
10
+ *
11
+ * `extractDeliverables` reads an owner's request for the separate things it
12
+ * asks for (numbered items, 「三个视频」, "one … another …") so partial
13
+ * delivery can be shown ("1 of 3 delivered").
14
+ *
15
+ * @module services/agent/stated-intent
16
+ */
17
+ const EN_NOW = [
18
+ // "I'm starting / breaking down / building … (now)"
19
+ /\b(?:i'?m|i am|we'?re|we are)\s+(?:now\s+|just\s+|about to\s+|going to\s+)?(?:starting|beginning|kicking off|breaking down|building|rebuilding|writing|making|creating|drafting|recording|rendering|editing|fixing|implementing|working on|doing|running|generating|replicating|redoing|putting together)\b/i,
20
+ // "next step is …", "the next step will be …"
21
+ /\b(?:the\s+)?next step(?:\s+(?:is|will be)|:)/i,
22
+ // "I'll start/build … now|right away|next|immediately"
23
+ /\b(?:i'?ll|i will|let me)\s+(?:now\s+)?(?:start|begin|build|make|create|write|do|draft|record|render|rebuild|fix|get (?:started|going)|work on|break down)\b[^.!?\n]*\b(?:now|right away|immediately|next|first|straight away)\b/i,
24
+ /\b(?:starting|beginning)\s+(?:now|right now|on (?:it|that|this))\b/i,
25
+ /\bnow\s+(?:building|writing|making|creating|drafting|recording|rendering|breaking|starting|working)\b/i,
26
+ ];
27
+ const ZH_NOW = /(我现在(?:就)?(?:开始|去|来|做|写|改|弄|拆|搭|录|剪|做)|我这就|现在就(?:开始|去|做|来)|马上(?:开始|去|做|来)|接下来(?:我)?(?:会|要|就)?[^,。\n]{0,12}|下一步(?:是|就是|我)|(?:做好|做完|弄好|写好|剪好|搞好)(?:后|了)?(?:就)?(?:发|给|交)你|开始(?:做|拆|写|搭|剪|录))/;
28
+ /** The agent is waiting on somebody else: not "doing it now". */
29
+ const WAITING = /\b(?:wait(?:ing)? (?:for|on)|once you|after you|when you (?:confirm|reply|answer|tell)|until you|if you (?:confirm|want|prefer|like|say)|let me know (?:if|which|whether))\b|(?:等你|等您|你确认|你回复|你告诉我|待你|您确认)/i;
30
+ /** A report of finished work, however it starts. */
31
+ const DONE_REPORT = /^\s*(?:done\b|finished\b|all done|已(?:经)?(?:完成|发|做好|搞定)|完成了|做好了|搞定了)/i;
32
+ /**
33
+ * Whether an agent post says it is starting work now.
34
+ *
35
+ * Returns null for finished-work reports, for text that waits on someone
36
+ * else, and for a post that ends in a question (the agent is asking, not
37
+ * starting).
38
+ *
39
+ * @param text - The agent's post to the owner
40
+ * @returns The stated intent, or null
41
+ *
42
+ * @example
43
+ * ```typescript
44
+ * detectStatedIntent("I'm breaking down the reference shot list now, then building the crab version.");
45
+ * // { sentence: "I'm breaking down the reference shot list now, …" }
46
+ * detectStatedIntent('Done — the video is attached.'); // null
47
+ * ```
48
+ */
49
+ export function detectStatedIntent(text) {
50
+ const t = (text ?? '').trim();
51
+ if (t.length < 4)
52
+ return null;
53
+ if (DONE_REPORT.test(t))
54
+ return null;
55
+ if (/[??]\s*$/.test(t))
56
+ return null;
57
+ const sentences = t.split(/(?<=[.!。!?\n])\s*/).map((s) => s.trim()).filter(Boolean);
58
+ for (const s of sentences) {
59
+ if (WAITING.test(s))
60
+ continue;
61
+ if (EN_NOW.some((re) => re.test(s)) || ZH_NOW.test(s)) {
62
+ // A sentence that is itself a past-tense report ("I've started …") never matches the patterns above.
63
+ return { sentence: s.length > 200 ? `${s.slice(0, 199)}…` : s };
64
+ }
65
+ }
66
+ return null;
67
+ }
68
+ // ---------------------------------------------------------------------------
69
+ // Deliverables
70
+ // ---------------------------------------------------------------------------
71
+ /** Most checklist items kept for one request. */
72
+ export const MAX_DELIVERABLES = 8;
73
+ const NUM_WORDS = {
74
+ 两: 2, 二: 2, 三: 3, 四: 4, 五: 5, 六: 6, two: 2, three: 3, four: 4, five: 5, six: 6,
75
+ };
76
+ const NOUN = '(?:videos?|clips?|posts?|articles?|reports?|images?|designs?|pages?|slides?|decks?|drafts?|versions?|docs?|documents?|mockups?|banners?|logos?|视频|文章|海报|报告|页面|版本|文档|方案|稿|帖子?|图片?|封面|配图)';
77
+ const COUNTED = new RegExp(`(?:^|[^\\d\\w])(\\d|两|二|三|四|五|六|two|three|four|five|six)\\s*(?:个|条|支|份|篇|张|套|组|版)?\\s*(${NOUN})`, 'i');
78
+ /**
79
+ * @param s - Item text
80
+ * @returns Whitespace-collapsed, clipped text
81
+ */
82
+ function clipItem(s) {
83
+ const one = s.replace(/\s+/g, ' ').trim().replace(/^[\s,;;,。.\-–—::]+|[\s,;;,。]+$/g, '');
84
+ return one.length > 140 ? `${one.slice(0, 139)}…` : one;
85
+ }
86
+ /**
87
+ * The separate things an owner's request asks for.
88
+ *
89
+ * Order of trust: explicit numbered/bulleted lists, inline "(1) … (2) …" /
90
+ * 「第一…第二…」 / "first … second …", "one … another …" / 「一个…另一个…」, then a
91
+ * bare count ("three videos" → three items). Returns [] when the request asks
92
+ * for one thing (or the shape is unclear): a checklist is only worth showing
93
+ * when partial delivery is possible.
94
+ *
95
+ * @param text - The owner's words
96
+ * @returns 2..{@link MAX_DELIVERABLES} items, or []
97
+ */
98
+ export function extractDeliverables(text) {
99
+ const t = (text ?? '').trim();
100
+ if (!t)
101
+ return [];
102
+ // 1. Lines that start with 1. / 1) / 1、 / ① / - / •
103
+ const lines = t.split(/\n/).map((l) => l.trim());
104
+ const listed = lines
105
+ .map((l) => /^(?:\(?[1-9]\d?[.))、::]|[①②③④⑤⑥⑦⑧⑨]|[-*•])\s*(.+)$/.exec(l)?.[1])
106
+ .filter((x) => !!x && x.trim().length > 1);
107
+ if (listed.length >= 2)
108
+ return listed.slice(0, MAX_DELIVERABLES).map(clipItem);
109
+ // 2. Inline numbering: "(1) a (2) b", "1) a 2) b", 「第一…第二…」, "first … second …"
110
+ const numbered = /(?:\(|()?\b1[.))、]|第一|\bfirst\b/i.test(t) && /(?:\(|()?\b2[.))、]|第二|\bsecond\b/i.test(t);
111
+ if (numbered) {
112
+ // Everything before the first marker is the preamble, not an item.
113
+ const items = t
114
+ .split(/(?:\(|()?\b[1-9][.))、]\s*|第[一二三四五六七八九]\s*[个条、,,::]?|\b(?:first|second|third|fourth)\b[,:]?\s*/i)
115
+ .slice(1)
116
+ .map(clipItem)
117
+ .filter((s) => s.length > 1);
118
+ if (items.length >= 2)
119
+ return items.slice(0, MAX_DELIVERABLES);
120
+ }
121
+ // 3. "one … another …" / 「一个…另一个…」
122
+ if (/\bone\b|一个/i.test(t) && /\banother\b|the other|a second|另一个|还有一个|再一个|另外一个/i.test(t)) {
123
+ const items = t
124
+ .split(/\b(?:one|another|the other|a second|a third)\b|另一个|还有一个|再一个|另外一个|一个/i)
125
+ .slice(1)
126
+ .map(clipItem)
127
+ .filter((s) => s.length > 1);
128
+ if (items.length >= 2)
129
+ return items.slice(0, MAX_DELIVERABLES);
130
+ }
131
+ // 4. A bare count: "three videos"
132
+ const m = COUNTED.exec(t);
133
+ if (m) {
134
+ const raw = (m[1] ?? '').toLowerCase();
135
+ const n = /^\d$/.test(raw) ? Number(raw) : NUM_WORDS[raw] ?? 0;
136
+ const noun = (m[2] ?? '').toLowerCase();
137
+ if (n >= 2 && n <= MAX_DELIVERABLES)
138
+ return Array.from({ length: n }, (_, i) => `${noun} ${i + 1} of ${n}`);
139
+ }
140
+ return [];
141
+ }
142
+ //# sourceMappingURL=stated-intent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stated-intent.js","sourceRoot":"","sources":["../../../../../../backend/src/services/agent/stated-intent.ts"],"names":[],"mappings":"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"}
@@ -253,6 +253,16 @@ export declare class OwnerThreadSentinelService {
253
253
  * @returns True when it owes an owner thread right now
254
254
  */
255
255
  owesRecently(agent: string): boolean;
256
+ /**
257
+ * Whether `agent` owes the owner an answer (unanswered for under
258
+ * CARD_THREAD_MAX_AGE_MS) or a promised result (within ACTIVE_WINDOW_MS) in a
259
+ * watched thread — hours, not the 30 minutes of {@link owesRecently}. An open card is not counted: then the owner owes
260
+ * the agent. Idle stops spare such an agent unless memory is in pressure.
261
+ *
262
+ * @param agent - The agent
263
+ * @returns True while it owes an owner thread
264
+ */
265
+ owesOwner(agent: string): boolean;
256
266
  /**
257
267
  * The owner thread `agent` owes right now (the newest one active within
258
268
  * CARD_THREAD_MAX_AGE_MS), for placing a card it asks.
@@ -1 +1 @@
1
- {"version":3,"file":"owner-thread-sentinel.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/messaging/owner-thread-sentinel.service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"owner-thread-sentinel.service.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/messaging/owner-thread-sentinel.service.ts"],"names":[],"mappings":"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"}
@@ -29,6 +29,7 @@ import { existsSync, mkdirSync, readFileSync, renameSync, writeFileSync } from '
29
29
  import * as path from 'path';
30
30
  import { ORCHESTRATOR_SESSION_NAME, OWNER_THREAD_SENTINEL_CONSTANTS as C } from '../../constants.js';
31
31
  import { LoggerService } from '../core/logger.service.js';
32
+ import { reportAgentPostForFollowThrough } from '../agent/follow-through.service.js';
32
33
  const EN_PROMISE = [
33
34
  /\b(?:i'?m|i am|we'?re|we are)\s+(?:now\s+|currently\s+|still\s+|just\s+)?(?:[a-z]+ing\b|on it|going to|about to)/i,
34
35
  /\b(?:i'?ll|i will|we'?ll|we will|let me|i'?m going to|will (?:report|update|follow up|get back|post|send|share|let you know|ping))\b/i,
@@ -381,6 +382,27 @@ export class OwnerThreadSentinelService {
381
382
  }
382
383
  return false;
383
384
  }
385
+ /**
386
+ * Whether `agent` owes the owner an answer (unanswered for under
387
+ * CARD_THREAD_MAX_AGE_MS) or a promised result (within ACTIVE_WINDOW_MS) in a
388
+ * watched thread — hours, not the 30 minutes of {@link owesRecently}. An open card is not counted: then the owner owes
389
+ * the agent. Idle stops spare such an agent unless memory is in pressure.
390
+ *
391
+ * @param agent - The agent
392
+ * @returns True while it owes an owner thread
393
+ */
394
+ owesOwner(agent) {
395
+ const now = this.now();
396
+ for (const e of this.entries.values()) {
397
+ if (e.agent !== agent)
398
+ continue;
399
+ if (e.ownerAt && (!e.agentPostAt || e.ownerAt > e.agentPostAt) && now - e.ownerAt <= C.CARD_THREAD_MAX_AGE_MS)
400
+ return true;
401
+ if (e.promise && now - e.promise.at <= C.ACTIVE_WINDOW_MS)
402
+ return true;
403
+ }
404
+ return false;
405
+ }
384
406
  /**
385
407
  * The owner thread `agent` owes right now (the newest one active within
386
408
  * CARD_THREAD_MAX_AGE_MS), for placing a card it asks.
@@ -693,6 +715,9 @@ export function reportOwnerThreadBlocking(agent, event) {
693
715
  * @param input - Who, where, what
694
716
  */
695
717
  export function reportOwnerThreadAgentPost(input) {
718
+ // The follow-through guard sees every agent post, including those in threads
719
+ // the sentinel does not watch (a decision card's thread, 2026-10-10).
720
+ reportAgentPostForFollowThrough({ agent: input.agent, text: input.text, ...(input.interim ? { interim: true } : {}) });
696
721
  try {
697
722
  instance?.noteAgentPost(input);
698
723
  }