agent-tempo 1.5.0 → 1.6.0
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/dashboard/package.json +1 -1
- package/dist/activities/outbox.d.ts +14 -1
- package/dist/activities/outbox.js +41 -0
- package/dist/cli/commands.js +11 -0
- package/dist/cli/config-command.d.ts +15 -0
- package/dist/cli/config-command.js +22 -7
- package/dist/client/core.js +15 -0
- package/dist/client/interface.d.ts +9 -0
- package/dist/config.d.ts +9 -0
- package/dist/config.js +9 -0
- package/dist/http/server.js +12 -1
- package/dist/pi/cue-pump.d.ts +86 -13
- package/dist/pi/cue-pump.js +102 -15
- package/dist/pi/extension.d.ts +29 -15
- package/dist/pi/extension.js +96 -19
- package/dist/pi/index.d.ts +2 -2
- package/dist/pi/index.js +2 -1
- package/dist/pi/pi-types.d.ts +50 -0
- package/dist/pi/reset-pump.d.ts +55 -17
- package/dist/pi/reset-pump.js +70 -20
- package/dist/server-tools.d.ts +7 -1
- package/dist/server-tools.js +2 -2
- package/dist/server.js +5 -2
- package/dist/spawn.d.ts +10 -0
- package/dist/spawn.js +7 -0
- package/dist/tools/recruit.d.ts +19 -2
- package/dist/tools/recruit.js +26 -2
- package/dist/tui/index.js +1 -0
- package/dist/utils/parent-death-watchdog.d.ts +12 -0
- package/dist/utils/parent-death-watchdog.js +25 -0
- package/package.json +1 -1
package/dist/server.js
CHANGED
|
@@ -72,7 +72,10 @@ async function main() {
|
|
|
72
72
|
await idleServer.connect(transport);
|
|
73
73
|
return;
|
|
74
74
|
}
|
|
75
|
-
|
|
75
|
+
// #676 FIX-1 — resolve config WITH sources so recruit can tell an operator-SET
|
|
76
|
+
// defaultAgent from the built-in 'claude' default. `.config` is the same Config
|
|
77
|
+
// getConfig() returns (behavior-preserving); `.sources.defaultAgent` is the origin.
|
|
78
|
+
const { config, sources } = (0, config_1.getConfigWithSources)();
|
|
76
79
|
const isConductor = process.env[config_1.ENV.CONDUCTOR] === 'true';
|
|
77
80
|
const requestedName = process.env[config_1.ENV.PLAYER_NAME] || '';
|
|
78
81
|
// Conductors use their requested name or fall back to 'conductor'.
|
|
@@ -289,7 +292,7 @@ async function main() {
|
|
|
289
292
|
// the same call. Adding a new tool? Add it once in `server-tools.ts`.
|
|
290
293
|
(0, server_tools_1.registerAllTempoTools)(mcpServer, {
|
|
291
294
|
client, config, getPlayerId, setPlayerId, handle, workflowId,
|
|
292
|
-
ownAgentType, isConductor,
|
|
295
|
+
ownAgentType, defaultAgentSource: sources.defaultAgent, isConductor,
|
|
293
296
|
});
|
|
294
297
|
const MAESTRO_ACK = '\n\n[IMPORTANT: This message is from a human (Maestro). Immediately cue the sender back with a brief acknowledgment and your planned next step before doing the work.]';
|
|
295
298
|
// Start message poller — push messages into Claude Code via channel notifications.
|
package/dist/spawn.d.ts
CHANGED
|
@@ -150,6 +150,16 @@ export interface CopilotBridgeOpts {
|
|
|
150
150
|
attachmentId?: string;
|
|
151
151
|
attachmentRunId?: string;
|
|
152
152
|
adapterId?: string;
|
|
153
|
+
/**
|
|
154
|
+
* #672 — set true by a TRANSIENT-CLI spawner that launches this bridge DETACHED
|
|
155
|
+
* to outlive it: BOTH the `up` conductor (commands.ts) AND the `up --lineup`
|
|
156
|
+
* copilot PLAYER loop (commands.ts applyLineupPlayersAndSchedules) — both spawn
|
|
157
|
+
* the bridge directly (no terminal), so its ppid is the short-lived CLI. When
|
|
158
|
+
* set, the bridge skips the ppid-poll that would self-kill it on the CLI's exit
|
|
159
|
+
* (stdin-EOF stays). The DAEMON-recruit path (outbox.ts) OMITS it → the bridge
|
|
160
|
+
* keeps the ppid-poll (#604 anti-leak on daemon death; ppid = persistent daemon).
|
|
161
|
+
*/
|
|
162
|
+
transientSpawner?: boolean;
|
|
153
163
|
}
|
|
154
164
|
export interface CopilotBridgeResult {
|
|
155
165
|
pid: number | undefined;
|
package/dist/spawn.js
CHANGED
|
@@ -518,6 +518,10 @@ function buildPiConductorSpawn(opts) {
|
|
|
518
518
|
[config_1.ENV.TASK_QUEUE]: opts.taskQueue,
|
|
519
519
|
[config_1.ENV.ENSEMBLE]: opts.ensemble,
|
|
520
520
|
[config_1.ENV.CONDUCTOR]: 'true', // codebase-consistent; the Pi extension accepts '1'|'true'
|
|
521
|
+
// #672 — the Pi conductor is launched detached by the transient `up` CLI:
|
|
522
|
+
// skip the ppid-poll (no current pi process installs the watchdog, but this is
|
|
523
|
+
// propagation-safe + principled if a pi subprocess ever does; stdin-EOF stays).
|
|
524
|
+
[config_1.ENV.NO_PPID_WATCHDOG]: '1',
|
|
521
525
|
[config_1.ENV.PLAYER_NAME]: opts.sessionName,
|
|
522
526
|
...(opts.devMode ? { [config_1.ENV.DEV_MODE]: '1' } : {}),
|
|
523
527
|
...(opts.anthropicApiKey ? { ANTHROPIC_API_KEY: opts.anthropicApiKey } : {}),
|
|
@@ -565,6 +569,9 @@ function spawnCopilotBridge(opts) {
|
|
|
565
569
|
[config_1.ENV.BRIDGE_MODE]: '', // Clear parent's bridge mode
|
|
566
570
|
[config_1.ENV.TEMPORAL_ADDRESS]: opts.temporalAddress,
|
|
567
571
|
[config_1.ENV.CONDUCTOR]: opts.isConductor ? 'true' : '',
|
|
572
|
+
// #672 — transient-CLI spawner: the detached bridge skips the ppid-poll
|
|
573
|
+
// (would self-kill on the short-lived `up` exit). Daemon recruit omits it.
|
|
574
|
+
...(opts.transientSpawner ? { [config_1.ENV.NO_PPID_WATCHDOG]: '1' } : {}),
|
|
568
575
|
// Forward Temporal connection settings so child processes can connect
|
|
569
576
|
...(opts.temporalNamespace ? { [config_1.ENV.TEMPORAL_NAMESPACE]: opts.temporalNamespace } : {}),
|
|
570
577
|
...(opts.temporalApiKey ? { [config_1.ENV.TEMPORAL_API_KEY]: opts.temporalApiKey } : {}),
|
package/dist/tools/recruit.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Client, WorkflowHandle } from '@temporalio/client';
|
|
2
|
-
import { Config } from '../config';
|
|
2
|
+
import { Config, ConfigSource } from '../config';
|
|
3
3
|
import { AgentType } from '../types';
|
|
4
4
|
import type { HostInfo } from '../types';
|
|
5
5
|
import { type TempoToolDescriptor } from './descriptor';
|
|
@@ -13,7 +13,24 @@ import { type TempoToolDescriptor } from './descriptor';
|
|
|
13
13
|
export interface RegisterRecruitToolDeps {
|
|
14
14
|
listHostsFn?: (client: Client) => Promise<HostInfo[]>;
|
|
15
15
|
}
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
|
+
* #676 FIX-1 — recruit agent precedence: explicit `argAgent` > operator-SET
|
|
18
|
+
* `configDefault` > `ownAgentType` (this player's mirror-fallback). The
|
|
19
|
+
* `defaultAgentSource` distinguishes an operator-set default (origin
|
|
20
|
+
* flag/env/config/temporal-cli) from the built-in 'claude' default (source
|
|
21
|
+
* 'default') / truly-unset ('none') — only an operator-set default wins over the
|
|
22
|
+
* mirror, so a copilot/pi conductor recruits its own kind by default. Pure +
|
|
23
|
+
* exported for unit testing.
|
|
24
|
+
*/
|
|
25
|
+
export declare function resolveRecruitAgent(argAgent: AgentType | undefined, configDefault: AgentType, defaultAgentSource: ConfigSource | undefined, ownAgentType: AgentType): AgentType;
|
|
26
|
+
export declare function buildRecruitTool(client: Client, config: Config, getPlayerId: () => string, handle: WorkflowHandle, ownAgentType?: AgentType,
|
|
27
|
+
/**
|
|
28
|
+
* #676 FIX-1 — the SOURCE of `config.defaultAgent` (from getConfigWithSources),
|
|
29
|
+
* used to distinguish an operator-SET default from the built-in 'claude'
|
|
30
|
+
* default (source 'default'). Undefined → treated as not-operator-set →
|
|
31
|
+
* recruit falls back to `ownAgentType` (preserves the pre-FIX-1 mirror).
|
|
32
|
+
*/
|
|
33
|
+
defaultAgentSource?: ConfigSource, deps?: RegisterRecruitToolDeps): TempoToolDescriptor;
|
|
17
34
|
/**
|
|
18
35
|
* Given a host liveness+profile snapshot, validate that `targetHost` is
|
|
19
36
|
* (a) known, (b) recruit-ready, and (c) advertises support for
|
package/dist/tools/recruit.js
CHANGED
|
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.resolveRecruitAgent = resolveRecruitAgent;
|
|
36
37
|
exports.buildRecruitTool = buildRecruitTool;
|
|
37
38
|
exports.checkHostPreflight = checkHostPreflight;
|
|
38
39
|
exports.nearestHostname = nearestHostname;
|
|
@@ -78,7 +79,30 @@ function hasOpencodeOnPath() {
|
|
|
78
79
|
return false;
|
|
79
80
|
}
|
|
80
81
|
}
|
|
81
|
-
|
|
82
|
+
/**
|
|
83
|
+
* #676 FIX-1 — recruit agent precedence: explicit `argAgent` > operator-SET
|
|
84
|
+
* `configDefault` > `ownAgentType` (this player's mirror-fallback). The
|
|
85
|
+
* `defaultAgentSource` distinguishes an operator-set default (origin
|
|
86
|
+
* flag/env/config/temporal-cli) from the built-in 'claude' default (source
|
|
87
|
+
* 'default') / truly-unset ('none') — only an operator-set default wins over the
|
|
88
|
+
* mirror, so a copilot/pi conductor recruits its own kind by default. Pure +
|
|
89
|
+
* exported for unit testing.
|
|
90
|
+
*/
|
|
91
|
+
function resolveRecruitAgent(argAgent, configDefault, defaultAgentSource, ownAgentType) {
|
|
92
|
+
if (argAgent)
|
|
93
|
+
return argAgent;
|
|
94
|
+
const operatorSet = !!defaultAgentSource
|
|
95
|
+
&& ['flag', 'env', 'config', 'temporal-cli'].includes(defaultAgentSource);
|
|
96
|
+
return operatorSet ? configDefault : ownAgentType;
|
|
97
|
+
}
|
|
98
|
+
function buildRecruitTool(client, config, getPlayerId, handle, ownAgentType = 'claude',
|
|
99
|
+
/**
|
|
100
|
+
* #676 FIX-1 — the SOURCE of `config.defaultAgent` (from getConfigWithSources),
|
|
101
|
+
* used to distinguish an operator-SET default from the built-in 'claude'
|
|
102
|
+
* default (source 'default'). Undefined → treated as not-operator-set →
|
|
103
|
+
* recruit falls back to `ownAgentType` (preserves the pre-FIX-1 mirror).
|
|
104
|
+
*/
|
|
105
|
+
defaultAgentSource, deps = {}) {
|
|
82
106
|
// Lazy default — only imports utils/hosts when actually called, so the
|
|
83
107
|
// MCP server's module load graph doesn't drag the whole join layer
|
|
84
108
|
// into every consumer at import time.
|
|
@@ -133,7 +157,7 @@ function buildRecruitTool(client, config, getPlayerId, handle, ownAgentType = 'c
|
|
|
133
157
|
handler: async (args) => {
|
|
134
158
|
const { workDir, name, initialMessage } = args;
|
|
135
159
|
const isConductor = args.conductor === true;
|
|
136
|
-
const agent = args.agent
|
|
160
|
+
const agent = resolveRecruitAgent(args.agent, config.defaultAgent, defaultAgentSource, ownAgentType);
|
|
137
161
|
const model = args.model;
|
|
138
162
|
const agentTypeName = args.type;
|
|
139
163
|
const systemPrompt = args.systemPrompt;
|
package/dist/tui/index.js
CHANGED
|
@@ -134,6 +134,7 @@ function createDummyClient() {
|
|
|
134
134
|
restore: fail,
|
|
135
135
|
isConnected: async () => false,
|
|
136
136
|
hasGlobalMaestro: async () => false,
|
|
137
|
+
ensembleExists: async () => false,
|
|
137
138
|
getSchedules: async () => [],
|
|
138
139
|
cancelSchedule: fail,
|
|
139
140
|
getEnsembleChat: async () => ({ messages: [], total: 0, hasMore: false, hasConductor: false }),
|
|
@@ -1 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Should the ppid-poll signal be installed? FALSE only when a TRANSIENT-CLI
|
|
3
|
+
* spawner set {@link ENV.NO_PPID_WATCHDOG} on a process it intentionally detached
|
|
4
|
+
* to OUTLIVE it (#672 — e.g. the short-lived `up` conductor: polling its dead pid
|
|
5
|
+
* would self-kill the conductor seconds after launch). Pure + injectable.
|
|
6
|
+
*
|
|
7
|
+
* Skipping ppid-poll is propagation-SAFE: the flag inherits down the spawn tree,
|
|
8
|
+
* but stdin-EOF (always installed) protects any child — its stdin IS this
|
|
9
|
+
* process's pipe, so it fires the instant THIS process dies. Only the ppid-poll
|
|
10
|
+
* (which keys on the SPAWNER, not the immediate parent) is the harmful signal.
|
|
11
|
+
*/
|
|
12
|
+
export declare function shouldInstallPpidPoll(env?: NodeJS.ProcessEnv): boolean;
|
|
1
13
|
export declare function installParentDeathWatchdog(): void;
|
|
@@ -23,15 +23,40 @@
|
|
|
23
23
|
// we falsely conclude the parent is alive. The stdin EOF path catches
|
|
24
24
|
// that case immediately, so this is purely a fallback.
|
|
25
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.shouldInstallPpidPoll = shouldInstallPpidPoll;
|
|
26
27
|
exports.installParentDeathWatchdog = installParentDeathWatchdog;
|
|
28
|
+
const config_1 = require("../config");
|
|
27
29
|
const log = (...args) => console.error('[agent-tempo:watchdog]', ...args);
|
|
30
|
+
/**
|
|
31
|
+
* Should the ppid-poll signal be installed? FALSE only when a TRANSIENT-CLI
|
|
32
|
+
* spawner set {@link ENV.NO_PPID_WATCHDOG} on a process it intentionally detached
|
|
33
|
+
* to OUTLIVE it (#672 — e.g. the short-lived `up` conductor: polling its dead pid
|
|
34
|
+
* would self-kill the conductor seconds after launch). Pure + injectable.
|
|
35
|
+
*
|
|
36
|
+
* Skipping ppid-poll is propagation-SAFE: the flag inherits down the spawn tree,
|
|
37
|
+
* but stdin-EOF (always installed) protects any child — its stdin IS this
|
|
38
|
+
* process's pipe, so it fires the instant THIS process dies. Only the ppid-poll
|
|
39
|
+
* (which keys on the SPAWNER, not the immediate parent) is the harmful signal.
|
|
40
|
+
*/
|
|
41
|
+
function shouldInstallPpidPoll(env = process.env) {
|
|
42
|
+
return env[config_1.ENV.NO_PPID_WATCHDOG] !== '1';
|
|
43
|
+
}
|
|
28
44
|
function installParentDeathWatchdog() {
|
|
29
45
|
const exit = (reason) => {
|
|
30
46
|
log('parent gone (', reason, ') — exiting');
|
|
31
47
|
process.exit(0);
|
|
32
48
|
};
|
|
49
|
+
// stdin-EOF — UNIVERSALLY correct + ALWAYS installed: a closed stdin pipe means
|
|
50
|
+
// the IMMEDIATE parent is gone. This is what reaps a detached process's OWN
|
|
51
|
+
// children even when ppid-poll is skipped (the child's stdin is our pipe).
|
|
33
52
|
process.stdin.on('end', () => exit('stdin end'));
|
|
34
53
|
process.stdin.on('close', () => exit('stdin close'));
|
|
54
|
+
// ppid-poll — keys on the SPAWNER's death. Correct for a long-lived daemon
|
|
55
|
+
// spawner (#604 anti-leak), HARMFUL for a transient CLI that detached us to
|
|
56
|
+
// outlive it (#672). Skipped when the spawner marked itself transient; the
|
|
57
|
+
// Temporal lease TTL reaps a genuinely-orphaned detached process instead.
|
|
58
|
+
if (!shouldInstallPpidPoll())
|
|
59
|
+
return;
|
|
35
60
|
const parentPid = process.ppid;
|
|
36
61
|
if (parentPid && parentPid > 1) {
|
|
37
62
|
const timer = setInterval(() => {
|