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.
- package/dist/backend/backend/src/controllers/chat/agent-reply.controller.d.ts.map +1 -1
- package/dist/backend/backend/src/controllers/chat/agent-reply.controller.js +5 -0
- package/dist/backend/backend/src/controllers/chat/agent-reply.controller.js.map +1 -1
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +18 -12
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/services/agent/active-work-briefing.service.d.ts +11 -0
- package/dist/backend/backend/src/services/agent/active-work-briefing.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/active-work-briefing.service.js +32 -11
- package/dist/backend/backend/src/services/agent/active-work-briefing.service.js.map +1 -1
- package/dist/backend/backend/src/services/agent/follow-through.service.d.ts +132 -0
- package/dist/backend/backend/src/services/agent/follow-through.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/agent/follow-through.service.js +199 -0
- package/dist/backend/backend/src/services/agent/follow-through.service.js.map +1 -0
- package/dist/backend/backend/src/services/agent/follow-through.wiring.d.ts +62 -0
- package/dist/backend/backend/src/services/agent/follow-through.wiring.d.ts.map +1 -0
- package/dist/backend/backend/src/services/agent/follow-through.wiring.js +158 -0
- package/dist/backend/backend/src/services/agent/follow-through.wiring.js.map +1 -0
- package/dist/backend/backend/src/services/agent/idle-detection.service.d.ts +24 -6
- package/dist/backend/backend/src/services/agent/idle-detection.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/idle-detection.service.js +46 -11
- package/dist/backend/backend/src/services/agent/idle-detection.service.js.map +1 -1
- package/dist/backend/backend/src/services/agent/idle-work-guard.d.ts +61 -0
- package/dist/backend/backend/src/services/agent/idle-work-guard.d.ts.map +1 -0
- package/dist/backend/backend/src/services/agent/idle-work-guard.js +81 -0
- package/dist/backend/backend/src/services/agent/idle-work-guard.js.map +1 -0
- package/dist/backend/backend/src/services/agent/stated-intent.d.ts +55 -0
- package/dist/backend/backend/src/services/agent/stated-intent.d.ts.map +1 -0
- package/dist/backend/backend/src/services/agent/stated-intent.js +142 -0
- package/dist/backend/backend/src/services/agent/stated-intent.js.map +1 -0
- package/dist/backend/backend/src/services/messaging/owner-thread-sentinel.service.d.ts +10 -0
- package/dist/backend/backend/src/services/messaging/owner-thread-sentinel.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/messaging/owner-thread-sentinel.service.js +25 -0
- package/dist/backend/backend/src/services/messaging/owner-thread-sentinel.service.js.map +1 -1
- package/dist/backend/backend/src/services/monitoring/agent-turn-state.d.ts +16 -0
- package/dist/backend/backend/src/services/monitoring/agent-turn-state.d.ts.map +1 -1
- package/dist/backend/backend/src/services/monitoring/agent-turn-state.js +31 -0
- package/dist/backend/backend/src/services/monitoring/agent-turn-state.js.map +1 -1
- package/dist/backend/backend/src/services/v3/ticket-review.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/ticket-review.service.js +5 -0
- package/dist/backend/backend/src/services/v3/ticket-review.service.js.map +1 -1
- package/dist/backend/backend/src/services/v3/ticket-work-guard.d.ts +81 -0
- package/dist/backend/backend/src/services/v3/ticket-work-guard.d.ts.map +1 -0
- package/dist/backend/backend/src/services/v3/ticket-work-guard.js +122 -0
- package/dist/backend/backend/src/services/v3/ticket-work-guard.js.map +1 -0
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/services/agent/active-work-briefing.service.d.ts +11 -0
- package/dist/cli/backend/src/services/agent/active-work-briefing.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/agent/active-work-briefing.service.js +32 -11
- package/dist/cli/backend/src/services/agent/active-work-briefing.service.js.map +1 -1
- package/dist/cli/backend/src/services/agent/follow-through.service.d.ts +132 -0
- package/dist/cli/backend/src/services/agent/follow-through.service.d.ts.map +1 -0
- package/dist/cli/backend/src/services/agent/follow-through.service.js +199 -0
- package/dist/cli/backend/src/services/agent/follow-through.service.js.map +1 -0
- package/dist/cli/backend/src/services/agent/stated-intent.d.ts +55 -0
- package/dist/cli/backend/src/services/agent/stated-intent.d.ts.map +1 -0
- package/dist/cli/backend/src/services/agent/stated-intent.js +142 -0
- package/dist/cli/backend/src/services/agent/stated-intent.js.map +1 -0
- package/dist/cli/backend/src/services/messaging/owner-thread-sentinel.service.d.ts +10 -0
- package/dist/cli/backend/src/services/messaging/owner-thread-sentinel.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/messaging/owner-thread-sentinel.service.js +25 -0
- package/dist/cli/backend/src/services/messaging/owner-thread-sentinel.service.js.map +1 -1
- package/dist/cli/backend/src/services/monitoring/agent-turn-state.d.ts +16 -0
- package/dist/cli/backend/src/services/monitoring/agent-turn-state.d.ts.map +1 -1
- package/dist/cli/backend/src/services/monitoring/agent-turn-state.js +31 -0
- package/dist/cli/backend/src/services/monitoring/agent-turn-state.js.map +1 -1
- package/dist/cli/backend/src/services/v3/ticket-review.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/v3/ticket-review.service.js +5 -0
- package/dist/cli/backend/src/services/v3/ticket-review.service.js.map +1 -1
- package/dist/cli/backend/src/services/v3/ticket-work-guard.d.ts +81 -0
- package/dist/cli/backend/src/services/v3/ticket-work-guard.d.ts.map +1 -0
- package/dist/cli/backend/src/services/v3/ticket-work-guard.js +122 -0
- package/dist/cli/backend/src/services/v3/ticket-work-guard.js.map +1 -0
- 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":"AAAA;;;;;;;;;;;;;;;GAeG;AAQH,MAAM,MAAM,GAAG;IACb,oDAAoD;IACpD,8SAA8S;IAC9S,8CAA8C;IAC9C,gDAAgD;IAChD,uDAAuD;IACvD,mOAAmO;IACnO,qEAAqE;IACrE,wGAAwG;CACzG,CAAC;AACF,MAAM,MAAM,GAAG,2LAA2L,CAAC;AAE3M,iEAAiE;AACjE,MAAM,OAAO,GAAG,6MAA6M,CAAC;AAE9N,oDAAoD;AACpD,MAAM,WAAW,GAAG,uEAAuE,CAAC;AAE5F;;;;;;;;;;;;;;;;GAgBG;AACH,MAAM,UAAU,kBAAkB,CAAC,IAAY;IAC9C,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,CAAC,CAAC,MAAM,GAAG,CAAC;QAAE,OAAO,IAAI,CAAC;IAC9B,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACrC,IAAI,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IACpF,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;QAC3B,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC;YAAE,SAAS;QAC9B,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;YACvD,qGAAqG;YACrG,OAAO,EAAE,QAAQ,EAAE,CAAC,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;QACjE,CAAC;IACF,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,8EAA8E;AAC9E,eAAe;AACf,8EAA8E;AAE9E,iDAAiD;AACjD,MAAM,CAAC,MAAM,gBAAgB,GAAG,CAAC,CAAC;AAElC,MAAM,SAAS,GAA2B;IACzC,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,EAAE,CAAC;CAC9E,CAAC;AACF,MAAM,IAAI,GAAG,yLAAyL,CAAC;AACvM,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,0FAA0F,IAAI,GAAG,EAAE,GAAG,CAAC,CAAC;AAEnI;;;GAGG;AACH,SAAS,QAAQ,CAAC,CAAS;IAC1B,MAAM,GAAG,GAAG,CAAC,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,iCAAiC,EAAE,EAAE,CAAC,CAAC;IACzF,OAAO,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC;AACzD,CAAC;AAED;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,mBAAmB,CAAC,IAAY;IAC/C,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC;IAC9B,IAAI,CAAC,CAAC;QAAE,OAAO,EAAE,CAAC;IAElB,oDAAoD;IACpD,MAAM,KAAK,GAAG,CAAC,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACjD,MAAM,MAAM,GAAG,KAAK;SAClB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,oDAAoD,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;SAC7E,MAAM,CAAC,CAAC,CAAC,EAAe,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACzD,IAAI,MAAM,CAAC,MAAM,IAAI,CAAC;QAAE,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAC;IAE/E,gFAAgF;IAChF,MAAM,QAAQ,GAAG,kCAAkC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,mCAAmC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC3G,IAAI,QAAQ,EAAE,CAAC;QACd,mEAAmE;QACnE,MAAM,KAAK,GAAG,CAAC;aACb,KAAK,CAAC,gGAAgG,CAAC;aACvG,KAAK,CAAC,CAAC,CAAC;aACR,GAAG,CAAC,QAAQ,CAAC;aACb,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC9B,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC;YAAE,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC;IAChE,CAAC;IAED,mCAAmC;IACnC,IAAI,aAAa,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,mDAAmD,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC;QAC1F,MAAM,KAAK,GAAG,CAAC;aACb,KAAK,CAAC,sEAAsE,CAAC;aAC7E,KAAK,CAAC,CAAC,CAAC;aACR,GAAG,CAAC,QAAQ,CAAC;aACb,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;QAC9B,IAAI,KAAK,CAAC,MAAM,IAAI,CAAC;YAAE,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,gBAAgB,CAAC,CAAC;IAChE,CAAC;IAED,kCAAkC;IAClC,MAAM,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAC1B,IAAI,CAAC,EAAE,CAAC;QACP,MAAM,GAAG,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QACvC,MAAM,CAAC,GAAG,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC/D,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;QACxC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,gBAAgB;YAAE,OAAO,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,IAAI,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IAC7G,CAAC;IACD,OAAO,EAAE,CAAC;AACX,CAAC"}
|
|
@@ -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":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;
|
|
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
|
}
|