@wrongstack/core 0.299.0 → 0.301.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/dist/chronicle/index.d.ts +2 -0
- package/dist/chronicle/index.js +297 -10
- package/dist/chronicle/project-server.js +253 -15
- package/dist/chronicle/sqlite-compaction.d.ts +20 -0
- package/dist/chronicle/sqlite-journal.d.ts +57 -0
- package/dist/coordination/agents/capability-manifest.d.ts +7 -0
- package/dist/coordination/agents/index.d.ts +3 -2
- package/dist/coordination/agents/index.js +818 -570
- package/dist/coordination/agents/project-agent-identity.d.ts +6 -6
- package/dist/coordination/agents/role-skills.d.ts +1 -1
- package/dist/coordination/agents/types.d.ts +6 -0
- package/dist/coordination/director.d.ts +10 -2
- package/dist/coordination/fleet-manager.d.ts +48 -3
- package/dist/coordination/ifleet-manager.d.ts +2 -0
- package/dist/coordination/index.js +6880 -6440
- package/dist/coordination/mail-tools.d.ts +3 -3
- package/dist/coordination/mailbox-project-server.js +3 -4
- package/dist/coordination/mailbox-types.d.ts +6 -0
- package/dist/coordination/multi-agent-coordinator.d.ts +1 -0
- package/dist/core/agent-response.d.ts +5 -1
- package/dist/core/agent-tools.d.ts +3 -0
- package/dist/core/context.d.ts +20 -0
- package/dist/core/fallback-model.d.ts +48 -0
- package/dist/core/index.d.ts +11 -9
- package/dist/core/index.js +1119 -310
- package/dist/core/instruction-template.d.ts +83 -0
- package/dist/core/next-steps-slot.d.ts +88 -0
- package/dist/core/system-prompt-blocks.d.ts +10 -1
- package/dist/core/system-prompt-builder.d.ts +40 -4
- package/dist/core/system-prompt-memory-skills.d.ts +1 -0
- package/dist/core/system-prompt-skill-bodies.d.ts +3 -3
- package/dist/defaults/index.js +9805 -8767
- package/dist/design/index.js +11 -3
- package/dist/execution/auto-compaction-middleware.d.ts +17 -0
- package/dist/execution/autonomy-brain.d.ts +15 -2
- package/dist/execution/brain-runtime.d.ts +3 -1
- package/dist/execution/compaction-core.d.ts +41 -2
- package/dist/execution/council-brain.d.ts +47 -1
- package/dist/execution/council-orchestrator.d.ts +25 -6
- package/dist/execution/council-prompts.d.ts +12 -1
- package/dist/execution/index.d.ts +11 -10
- package/dist/execution/index.js +10507 -9189
- package/dist/execution/tool-executor.d.ts +4 -1
- package/dist/execution/topic-shift-advisor.d.ts +53 -0
- package/dist/extension/index.js +8 -2
- package/dist/extension/registry.d.ts +8 -1
- package/dist/fleet-notifier.d.ts +9 -2
- package/dist/goal/index.js +11 -3
- package/dist/hooks/index.js +50 -13
- package/dist/hooks/runner.d.ts +12 -1
- package/dist/hq/index.js +59 -21
- package/dist/hq/protocol/fleet.d.ts +20 -0
- package/dist/hq/protocol.js +10 -0
- package/dist/hq/publisher.d.ts +21 -3
- package/dist/index.d.ts +22 -20
- package/dist/index.js +12211 -8572
- package/dist/infrastructure/index.js +108 -21
- package/dist/kernel/events/brain-events.d.ts +9 -0
- package/dist/kernel/events/provider-events.d.ts +42 -1
- package/dist/kernel/events/session-events.d.ts +13 -0
- package/dist/kernel/events/tool-events.d.ts +3 -3
- package/dist/models/index.d.ts +1 -0
- package/dist/models/index.js +18 -1
- package/dist/models/provider-credentials.d.ts +54 -0
- package/dist/plugin/api.d.ts +6 -0
- package/dist/plugin/config.d.ts +55 -0
- package/dist/plugin/index.d.ts +2 -1
- package/dist/plugin/index.js +2179 -544
- package/dist/plugins/auto-review-plugin.d.ts +3 -0
- package/dist/plugins/cloud-config-sync-plugin.d.ts +22 -0
- package/dist/plugins/review-claim-registry.d.ts +23 -8
- package/dist/plugins/review-types.d.ts +2 -0
- package/dist/registry/index.js +109 -74
- package/dist/registry/tool-registry.d.ts +15 -0
- package/dist/security/capabilities.d.ts +2 -0
- package/dist/security/index.d.ts +2 -2
- package/dist/security/index.js +166 -44
- package/dist/security/permission-helpers.d.ts +23 -6
- package/dist/security/permission-policy.d.ts +16 -0
- package/dist/security/readonly-permission-policy.d.ts +20 -0
- package/dist/security/totp.d.ts +14 -0
- package/dist/session-registry-atomic-file.d.ts +32 -5
- package/dist/skills/frontmatter.d.ts +6 -0
- package/dist/skills/index.js +22 -5
- package/dist/storage/cloud-config-sync/sanitize.d.ts +54 -0
- package/dist/storage/cloud-config-sync.d.ts +87 -0
- package/dist/storage/director-state.d.ts +7 -0
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/index.js +887 -74
- package/dist/tools/fallback-system-config-view-tool.d.ts +1 -1
- package/dist/tools/index.d.ts +1 -1
- package/dist/tools/index.js +898 -335
- package/dist/tools/one-shot-llm-tool.d.ts +1 -0
- package/dist/tools/plugin-manager.d.ts +27 -0
- package/dist/types/config/mcp-features.d.ts +18 -11
- package/dist/types/config/root.d.ts +8 -1
- package/dist/types/config/runtime.d.ts +20 -6
- package/dist/types/config/skills-fleet-brain.d.ts +12 -4
- package/dist/types/config/tools.d.ts +16 -0
- package/dist/types/context-window.d.ts +1 -0
- package/dist/types/council.d.ts +11 -0
- package/dist/types/hooks.d.ts +14 -0
- package/dist/types/index.d.ts +3 -3
- package/dist/types/index.js +1 -0
- package/dist/types/multi-agent.d.ts +12 -0
- package/dist/types/one-shot-llm.d.ts +25 -0
- package/dist/types/plugin.d.ts +28 -0
- package/dist/types/provider.d.ts +16 -0
- package/dist/types/runtime-capability-manifest.d.ts +168 -0
- package/dist/types/skill.d.ts +5 -0
- package/dist/types/system-prompt.d.ts +3 -1
- package/dist/types/tool-executor.d.ts +8 -1
- package/dist/utils/context-breakdown.d.ts +8 -2
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils/index.js +141 -31
- package/dist/utils/regex-guard.d.ts +16 -2
- package/dist/utils/sage-output-block.d.ts +7 -10
- package/dist/utils/wstack-paths.d.ts +11 -3
- package/dist/worktree/index.js +4 -4
- package/instructions/agents/browser.md +1 -4
- package/instructions/agents/e2e.md +17 -15
- package/instructions/agents/ios.md +3 -3
- package/instructions/agents/search.md +1 -1
- package/instructions/autonomy/goal-preamble.md +4 -4
- package/instructions/coordination/director-preamble.md +2 -3
- package/instructions/coordination/subagent-baseline.md +3 -1
- package/instructions/leader-after-task.md +12 -0
- package/instructions/llm/chimera-review.md +1 -1
- package/instructions/modes/audit-lite.md +1 -1
- package/instructions/sections/tool/common-patterns.md +18 -2
- package/instructions/sections/tool/mailbox-compact.md +1 -1
- package/instructions/sections/tool/mailbox-full.md +1 -1
- package/instructions/system-lite.md +91 -3
- package/instructions/system-pro.md +296 -102
- package/instructions/system.md +248 -92
- package/package.json +3 -3
- package/skills/api-design/SKILL.md +3 -0
- package/skills/audit-log/SKILL.md +2 -0
- package/skills/auto-review/SKILL.md +6 -1
- package/skills/bug-hunter/SKILL.md +4 -1
- package/skills/chimera/SKILL.md +3 -0
- package/skills/data-governance/SKILL.md +3 -0
- package/skills/design-system/SKILL.md +5 -2
- package/skills/docker-deploy/SKILL.md +2 -0
- package/skills/git-flow/SKILL.md +2 -0
- package/skills/mailbox-bridge/SKILL.md +3 -0
- package/skills/mnemosyne/SKILL.md +2 -0
- package/skills/multi-agent/SKILL.md +4 -1
- package/skills/node-modern/SKILL.md +4 -1
- package/skills/observability/SKILL.md +3 -0
- package/skills/output-standards/SKILL.md +2 -0
- package/skills/plugin-author/SKILL.md +4 -1
- package/skills/prompt-engineering/SKILL.md +3 -1
- package/skills/react-modern/SKILL.md +6 -3
- package/skills/refactor-planner/SKILL.md +2 -0
- package/skills/research-web/SKILL.md +3 -0
- package/skills/sdd/SKILL.md +3 -1
- package/skills/security-scanner/SKILL.md +3 -0
- package/skills/skill-creator/SKILL.md +2 -0
- package/skills/tech-stack/SKILL.md +3 -0
- package/skills/testing/SKILL.md +4 -1
- package/skills/typescript-strict/SKILL.md +4 -1
- package/skills/wrongstack-kanban/SKILL.md +6 -3
- package/skills/wrongstack-mailbox/SKILL.md +4 -1
- package/skills/wrongstack-mailbox-mcp/SKILL.md +10 -8
|
@@ -67,7 +67,10 @@ export declare class ToolExecutor {
|
|
|
67
67
|
* Also installs the governed bridge because confirmed tools are re-run
|
|
68
68
|
* through this method after a pending confirmation.
|
|
69
69
|
*/
|
|
70
|
-
executeTool(tool: Tool, use: ToolUseBlock, ctx: Context, budget: number
|
|
70
|
+
executeTool(tool: Tool, use: ToolUseBlock, ctx: Context, budget: number, preToolContext?: {
|
|
71
|
+
text: string;
|
|
72
|
+
contextAs: 'inline' | 'separate';
|
|
73
|
+
}): Promise<{
|
|
71
74
|
block: ToolResultBlock;
|
|
72
75
|
bytes: number;
|
|
73
76
|
}>;
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import type { Context } from '../core/context.js';
|
|
2
|
+
import { type Message, type Provider } from '../types/index.js';
|
|
3
|
+
export interface TopicShiftAdvice {
|
|
4
|
+
suggestNewContext: boolean;
|
|
5
|
+
confidence: number;
|
|
6
|
+
reason: string;
|
|
7
|
+
nextTopic?: string | undefined;
|
|
8
|
+
source: 'explicit' | 'model' | 'cache' | 'local';
|
|
9
|
+
}
|
|
10
|
+
export interface TopicShiftAdvisorInput {
|
|
11
|
+
prompt: string;
|
|
12
|
+
messages: readonly Message[];
|
|
13
|
+
provider?: Provider | undefined;
|
|
14
|
+
model?: string | undefined;
|
|
15
|
+
contextTokens?: number | undefined;
|
|
16
|
+
maxContext?: number | undefined;
|
|
17
|
+
signal?: AbortSignal | undefined;
|
|
18
|
+
/** Notifies prompt surfaces only when the remote ambiguity check actually runs. */
|
|
19
|
+
onModelCheck?: ((busy: boolean) => void) | undefined;
|
|
20
|
+
}
|
|
21
|
+
export interface TopicShiftAdvisorOptions {
|
|
22
|
+
capacity?: number | undefined;
|
|
23
|
+
ttlMs?: number | undefined;
|
|
24
|
+
timeoutMs?: number | undefined;
|
|
25
|
+
now?: (() => number) | undefined;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Best-effort, bounded topic-boundary advisor for prompt-submit surfaces.
|
|
29
|
+
*
|
|
30
|
+
* Most prompts finish in the local gate. A separate provider call happens
|
|
31
|
+
* only after history is substantial AND the new prompt has little lexical
|
|
32
|
+
* continuity with recent user goals. Results are held in a small TTL/LRU so
|
|
33
|
+
* edit/retry or duplicate submits never pay twice.
|
|
34
|
+
*/
|
|
35
|
+
export declare class TopicShiftAdvisor {
|
|
36
|
+
private readonly capacity;
|
|
37
|
+
private readonly ttlMs;
|
|
38
|
+
private readonly timeoutMs;
|
|
39
|
+
private readonly now;
|
|
40
|
+
private readonly cache;
|
|
41
|
+
constructor(options?: TopicShiftAdvisorOptions);
|
|
42
|
+
advise(input: TopicShiftAdvisorInput): Promise<TopicShiftAdvice>;
|
|
43
|
+
private classify;
|
|
44
|
+
private get;
|
|
45
|
+
private set;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Start a clean model context without deleting the session journal or visible
|
|
49
|
+
* chat. Durable memory, provider/model selection, settings and project state
|
|
50
|
+
* remain owned by their existing services.
|
|
51
|
+
*/
|
|
52
|
+
export declare function startFreshTopicContext(ctx: Context): Promise<void>;
|
|
53
|
+
//# sourceMappingURL=topic-shift-advisor.d.ts.map
|
package/dist/extension/index.js
CHANGED
|
@@ -258,9 +258,15 @@ var ExtensionRegistry = class {
|
|
|
258
258
|
* Build a composed provider runner. Extensions with `wrapProviderRunner`
|
|
259
259
|
* form a middleware-style chain: the innermost extension wraps the
|
|
260
260
|
* default runner, each subsequent wrapper wraps the previous.
|
|
261
|
+
*
|
|
262
|
+
* Utility calls may exclude agent-loop-only middleware (notably
|
|
263
|
+
* `fallback-model`) while retaining request-safety wrappers such as the
|
|
264
|
+
* prompt firewall. One-shot orchestration owns its provider and fallback
|
|
265
|
+
* route, so an agent-loop fallback wrapper cannot safely run against it.
|
|
261
266
|
*/
|
|
262
|
-
wrapProviderRunner(inner) {
|
|
263
|
-
const
|
|
267
|
+
wrapProviderRunner(inner, options) {
|
|
268
|
+
const excluded = new Set(options?.exclude);
|
|
269
|
+
const wrappers = this.extensions.filter((e) => e.wrapProviderRunner && !excluded.has(e.name)).map((e) => ({ name: e.name, wrap: expectDefined(e.wrapProviderRunner) }));
|
|
264
270
|
if (wrappers.length === 0) return inner;
|
|
265
271
|
let composed = inner;
|
|
266
272
|
for (let i = wrappers.length - 1; i >= 0; i--) {
|
|
@@ -78,8 +78,15 @@ export declare class ExtensionRegistry {
|
|
|
78
78
|
* Build a composed provider runner. Extensions with `wrapProviderRunner`
|
|
79
79
|
* form a middleware-style chain: the innermost extension wraps the
|
|
80
80
|
* default runner, each subsequent wrapper wraps the previous.
|
|
81
|
+
*
|
|
82
|
+
* Utility calls may exclude agent-loop-only middleware (notably
|
|
83
|
+
* `fallback-model`) while retaining request-safety wrappers such as the
|
|
84
|
+
* prompt firewall. One-shot orchestration owns its provider and fallback
|
|
85
|
+
* route, so an agent-loop fallback wrapper cannot safely run against it.
|
|
81
86
|
*/
|
|
82
|
-
wrapProviderRunner(inner: ProviderRunnerFn
|
|
87
|
+
wrapProviderRunner(inner: ProviderRunnerFn, options?: {
|
|
88
|
+
exclude?: readonly string[] | undefined;
|
|
89
|
+
}): ProviderRunnerFn;
|
|
83
90
|
runBeforeToolExecution(...args: Parameters<BeforeToolExecutionHook>): Promise<Parameters<BeforeToolExecutionHook>[1]>;
|
|
84
91
|
runAfterToolExecution(...args: Parameters<AfterToolExecutionHook>): Promise<void>;
|
|
85
92
|
}
|
package/dist/fleet-notifier.d.ts
CHANGED
|
@@ -5,8 +5,13 @@ export interface FleetNotifierOptions {
|
|
|
5
5
|
projectRoot: string;
|
|
6
6
|
/** This process's pid, so a WebUI never pings itself. */
|
|
7
7
|
selfPid?: number | undefined;
|
|
8
|
-
/**
|
|
9
|
-
|
|
8
|
+
/**
|
|
9
|
+
* Injectable POST for tests. Defaults to a timed `fetch`.
|
|
10
|
+
*
|
|
11
|
+
* The second parameter carries the target's API token (H3). It is optional
|
|
12
|
+
* so an existing `(url) => …` stub stays assignable.
|
|
13
|
+
*/
|
|
14
|
+
post?: ((url: string, token?: string | undefined) => Promise<void>) | undefined;
|
|
10
15
|
}
|
|
11
16
|
export declare class FleetNotifier {
|
|
12
17
|
private readonly baseDir;
|
|
@@ -21,6 +26,8 @@ export declare class FleetNotifier {
|
|
|
21
26
|
notify(): void;
|
|
22
27
|
/** Resolve same-project WebUI ping URLs (cached briefly). Exposed for tests. */
|
|
23
28
|
endpoints(): Promise<string[]>;
|
|
29
|
+
/** Ping targets with their tokens (cached briefly). */
|
|
30
|
+
private targets;
|
|
24
31
|
dispose(): void;
|
|
25
32
|
/** Re-scope notifications after an in-process project switch. */
|
|
26
33
|
setProjectRoot(projectRoot: string): void;
|
package/dist/goal/index.js
CHANGED
|
@@ -2765,6 +2765,9 @@ function canonicalProjectRoot(absRoot) {
|
|
|
2765
2765
|
const commonDirFile = path4.join(gitDir, "commondir");
|
|
2766
2766
|
if (!fs.statSync(commonDirFile).isFile()) return checkoutRoot;
|
|
2767
2767
|
const commonDir = path4.resolve(gitDir, fs.readFileSync(commonDirFile, "utf8").trim());
|
|
2768
|
+
const worktreesDir = path4.dirname(gitDir);
|
|
2769
|
+
if (path4.basename(worktreesDir).toLowerCase() !== "worktrees") return checkoutRoot;
|
|
2770
|
+
if (path4.resolve(worktreesDir, "..") !== commonDir) return checkoutRoot;
|
|
2768
2771
|
if (path4.basename(commonDir).toLowerCase() !== ".git") return checkoutRoot;
|
|
2769
2772
|
return path4.dirname(commonDir);
|
|
2770
2773
|
} catch {
|
|
@@ -2787,7 +2790,7 @@ function safeProfileName(name) {
|
|
|
2787
2790
|
const safe = (name ?? "").replace(/[/\\:]/g, "_").replace(/\.\./g, "_").trim();
|
|
2788
2791
|
return safe || "default";
|
|
2789
2792
|
}
|
|
2790
|
-
function
|
|
2793
|
+
function bootstrapProfileName(globalRoot) {
|
|
2791
2794
|
try {
|
|
2792
2795
|
const parsed = JSON.parse(fs.readFileSync(path4.join(globalRoot, "config.json"), "utf8"));
|
|
2793
2796
|
return safeProfileName(
|
|
@@ -2805,7 +2808,7 @@ function wstackGlobalRoot() {
|
|
|
2805
2808
|
function resolveWstackPaths(opts) {
|
|
2806
2809
|
const globalRoot = opts.globalRoot ?? (opts.userHome ? path4.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
|
|
2807
2810
|
const homeDir = opts.userHome ?? os.homedir();
|
|
2808
|
-
const profileName = safeProfileName(opts.profileName ??
|
|
2811
|
+
const profileName = safeProfileName(opts.profileName ?? bootstrapProfileName(globalRoot));
|
|
2809
2812
|
const profileDir = path4.join(globalRoot, "profiles", profileName);
|
|
2810
2813
|
const hash = projectHash(opts.projectRoot);
|
|
2811
2814
|
const slug = projectSlug(opts.projectRoot);
|
|
@@ -2875,7 +2878,12 @@ function resolveWstackPaths(opts) {
|
|
|
2875
2878
|
projectRequirementIntakes: path4.join(projectDir, "requirement-intakes"),
|
|
2876
2879
|
syncConfig: path4.join(profileDir, "sync.json"),
|
|
2877
2880
|
configHistoryDir: path4.join(globalRoot, "config-history"),
|
|
2878
|
-
projectStatus: (
|
|
2881
|
+
projectStatus: (statusProjectSlug) => {
|
|
2882
|
+
if (!/^[a-z0-9](?:[a-z0-9-]{0,39})-[a-f0-9]{6}$/.test(statusProjectSlug)) {
|
|
2883
|
+
throw new Error(`Invalid project slug: ${statusProjectSlug}`);
|
|
2884
|
+
}
|
|
2885
|
+
return path4.join(globalRoot, "projects", statusProjectSlug, "status.json");
|
|
2886
|
+
}
|
|
2879
2887
|
};
|
|
2880
2888
|
}
|
|
2881
2889
|
|
package/dist/hooks/index.js
CHANGED
|
@@ -776,20 +776,36 @@ function hookMatcherMatches(matcher, toolName) {
|
|
|
776
776
|
|
|
777
777
|
// src/hooks/runner.ts
|
|
778
778
|
var DEFAULT_TIMEOUT_MS3 = 5e3;
|
|
779
|
+
var DEFAULT_BACKGROUND_TIMEOUT_MS = 6e4;
|
|
779
780
|
var MAX_TIMEOUT_MS = 10 * 6e4;
|
|
781
|
+
function hookTimeoutMs(entry) {
|
|
782
|
+
return entry.timeoutMs ?? (entry.background ? DEFAULT_BACKGROUND_TIMEOUT_MS : DEFAULT_TIMEOUT_MS3);
|
|
783
|
+
}
|
|
784
|
+
function contextFromOutcome(outcome) {
|
|
785
|
+
return {
|
|
786
|
+
...outcome.additionalContext ? { additionalContext: outcome.additionalContext } : {},
|
|
787
|
+
...outcome.contextAs ? { contextAs: outcome.contextAs } : {}
|
|
788
|
+
};
|
|
789
|
+
}
|
|
780
790
|
function normalizePreToolOutcome(outcome) {
|
|
781
|
-
|
|
791
|
+
const context = contextFromOutcome(outcome);
|
|
792
|
+
if ("action" in outcome) return { ...outcome, ...context };
|
|
782
793
|
if (outcome.decision === "block") {
|
|
783
|
-
return {
|
|
794
|
+
return {
|
|
795
|
+
action: "deny",
|
|
796
|
+
reason: outcome.reason?.trim() || "blocked by PreToolUse hook",
|
|
797
|
+
...context
|
|
798
|
+
};
|
|
784
799
|
}
|
|
785
800
|
if (outcome.modifiedInput && typeof outcome.modifiedInput === "object") {
|
|
786
801
|
return {
|
|
787
802
|
action: "mutate",
|
|
788
803
|
input: outcome.modifiedInput,
|
|
789
|
-
...outcome.reason ? { reason: outcome.reason } : {}
|
|
804
|
+
...outcome.reason ? { reason: outcome.reason } : {},
|
|
805
|
+
...context
|
|
790
806
|
};
|
|
791
807
|
}
|
|
792
|
-
return { action: "allow" };
|
|
808
|
+
return { action: "allow", ...context };
|
|
793
809
|
}
|
|
794
810
|
function malformedOutcome(outcome) {
|
|
795
811
|
if (!("action" in outcome)) return void 0;
|
|
@@ -819,11 +835,26 @@ var HookRunner = class {
|
|
|
819
835
|
has(event) {
|
|
820
836
|
return this.registry.has(event);
|
|
821
837
|
}
|
|
822
|
-
async preToolUse(toolName, toolInput, env) {
|
|
838
|
+
async preToolUse(toolName, toolInput, env, toolMeta) {
|
|
823
839
|
const entries = this.matching("PreToolUse", toolName);
|
|
824
840
|
if (entries.length === 0) return {};
|
|
841
|
+
const declared = {
|
|
842
|
+
...toolMeta?.capabilities ? { toolCapabilities: toolMeta.capabilities } : {},
|
|
843
|
+
...toolMeta?.mutating === void 0 ? {} : { toolMutating: toolMeta.mutating }
|
|
844
|
+
};
|
|
825
845
|
let current = toolInput;
|
|
826
846
|
let mutated = false;
|
|
847
|
+
const contexts = [];
|
|
848
|
+
let contextAs;
|
|
849
|
+
const collectContext = (outcome) => {
|
|
850
|
+
if (outcome.additionalContext) contexts.push(outcome.additionalContext);
|
|
851
|
+
if (outcome.contextAs === "separate") contextAs = "separate";
|
|
852
|
+
else if (outcome.contextAs === "inline" && contextAs === void 0) contextAs = "inline";
|
|
853
|
+
};
|
|
854
|
+
const resultContext = () => ({
|
|
855
|
+
...contexts.length > 0 ? { additionalContext: contexts.join("\n") } : {},
|
|
856
|
+
...contextAs ? { contextAs } : {}
|
|
857
|
+
});
|
|
827
858
|
const mutators = entries.filter((entry) => entry.stage === "mutate");
|
|
828
859
|
const validators = entries.filter((entry) => entry.stage === "validate");
|
|
829
860
|
for (const entry of mutators) {
|
|
@@ -831,13 +862,15 @@ var HookRunner = class {
|
|
|
831
862
|
event: "PreToolUse",
|
|
832
863
|
toolName,
|
|
833
864
|
toolInput: current,
|
|
865
|
+
...declared,
|
|
834
866
|
...this.base(env)
|
|
835
867
|
};
|
|
836
868
|
const outcome = await this.invoke(entry, payload, env);
|
|
837
869
|
if (!outcome) continue;
|
|
838
870
|
const normalized = normalizePreToolOutcome(outcome);
|
|
871
|
+
collectContext(normalized);
|
|
839
872
|
if (normalized.action === "deny") {
|
|
840
|
-
return { block: true, reason: normalized.reason };
|
|
873
|
+
return { block: true, reason: normalized.reason, ...resultContext() };
|
|
841
874
|
}
|
|
842
875
|
if (normalized.action === "mutate") {
|
|
843
876
|
current = normalized.input;
|
|
@@ -849,21 +882,25 @@ var HookRunner = class {
|
|
|
849
882
|
event: "PreToolUse",
|
|
850
883
|
toolName,
|
|
851
884
|
toolInput: current,
|
|
885
|
+
...declared,
|
|
852
886
|
...this.base(env)
|
|
853
887
|
};
|
|
854
888
|
const outcome = await this.invoke(entry, payload, env);
|
|
855
889
|
if (!outcome) continue;
|
|
856
890
|
const normalized = normalizePreToolOutcome(outcome);
|
|
891
|
+
collectContext(normalized);
|
|
857
892
|
if (normalized.action === "deny") {
|
|
858
|
-
return { block: true, reason: normalized.reason };
|
|
893
|
+
return { block: true, reason: normalized.reason, ...resultContext() };
|
|
859
894
|
}
|
|
860
895
|
if (normalized.action === "mutate") {
|
|
861
896
|
const reason = `PreToolUse validator "${entry.name}" attempted to mutate tool arguments`;
|
|
862
897
|
this.opts.logger?.warn?.(reason);
|
|
863
|
-
if (entry.failurePolicy === "closed")
|
|
898
|
+
if (entry.failurePolicy === "closed") {
|
|
899
|
+
return { block: true, reason, ...resultContext() };
|
|
900
|
+
}
|
|
864
901
|
}
|
|
865
902
|
}
|
|
866
|
-
return mutated ? { input: current } : {};
|
|
903
|
+
return { ...mutated ? { input: current } : {}, ...resultContext() };
|
|
867
904
|
}
|
|
868
905
|
async postToolUse(toolName, toolInput, result, env) {
|
|
869
906
|
const payload = {
|
|
@@ -961,12 +998,12 @@ var HookRunner = class {
|
|
|
961
998
|
result = await this.invokeInProcess(entry, payload, env);
|
|
962
999
|
} else {
|
|
963
1000
|
result = entry.kind === "shell" ? await runShellHookDetailed(
|
|
964
|
-
{ command: entry.command, timeoutMs: entry
|
|
1001
|
+
{ command: entry.command, timeoutMs: hookTimeoutMs(entry) },
|
|
965
1002
|
payload,
|
|
966
1003
|
this.opts.logger,
|
|
967
1004
|
{ signal: env.signal }
|
|
968
1005
|
) : await runHttpHookDetailed(
|
|
969
|
-
{ url: entry.url, headers: entry.headers, timeoutMs: entry
|
|
1006
|
+
{ url: entry.url, headers: entry.headers, timeoutMs: hookTimeoutMs(entry) },
|
|
970
1007
|
payload,
|
|
971
1008
|
this.opts.logger,
|
|
972
1009
|
{ signal: env.signal }
|
|
@@ -983,12 +1020,12 @@ var HookRunner = class {
|
|
|
983
1020
|
}
|
|
984
1021
|
if (!result.failure) return result.outcome;
|
|
985
1022
|
this.opts.logger?.warn?.(
|
|
986
|
-
`${payload.event} hook "${entry.name}" failed (${result.failure.kind}): ${result.failure.message}`
|
|
1023
|
+
`${payload.event} ${entry.background ? "background " : ""}hook "${entry.name}" failed (${result.failure.kind}): ${result.failure.message}`
|
|
987
1024
|
);
|
|
988
1025
|
return this.failureOutcome(entry, payload, result.failure);
|
|
989
1026
|
}
|
|
990
1027
|
async invokeInProcess(entry, payload, env) {
|
|
991
|
-
const timeoutMs = Math.max(1, Math.min(entry
|
|
1028
|
+
const timeoutMs = Math.max(1, Math.min(hookTimeoutMs(entry), MAX_TIMEOUT_MS));
|
|
992
1029
|
const controller = new AbortController();
|
|
993
1030
|
let timedOut = false;
|
|
994
1031
|
const onParentAbort = () => controller.abort(env.signal?.reason);
|
package/dist/hooks/runner.d.ts
CHANGED
|
@@ -22,6 +22,8 @@ export interface PreToolUseResult {
|
|
|
22
22
|
reason?: string | undefined;
|
|
23
23
|
/** Present only when a hook replaced the tool input. */
|
|
24
24
|
input?: Record<string, unknown>;
|
|
25
|
+
additionalContext?: string | undefined;
|
|
26
|
+
contextAs?: 'inline' | 'separate' | undefined;
|
|
25
27
|
}
|
|
26
28
|
export interface PromptResult {
|
|
27
29
|
block?: boolean | undefined;
|
|
@@ -46,7 +48,16 @@ export declare class HookRunner {
|
|
|
46
48
|
constructor(opts: HookRunnerOptions);
|
|
47
49
|
/** Cheap guard so callers can skip building payloads when nothing listens. */
|
|
48
50
|
has(event: HookEvent): boolean;
|
|
49
|
-
preToolUse(toolName: string, toolInput: Record<string, unknown>, env: HookRunEnv
|
|
51
|
+
preToolUse(toolName: string, toolInput: Record<string, unknown>, env: HookRunEnv,
|
|
52
|
+
/**
|
|
53
|
+
* What the tool declares about itself. Optional so existing callers (and
|
|
54
|
+
* tests) keep working, but the executor always passes it — a policy hook
|
|
55
|
+
* that keys on capability instead of tool name needs it to be there.
|
|
56
|
+
*/
|
|
57
|
+
toolMeta?: {
|
|
58
|
+
capabilities?: readonly string[] | undefined;
|
|
59
|
+
mutating?: boolean | undefined;
|
|
60
|
+
}): Promise<PreToolUseResult>;
|
|
50
61
|
postToolUse(toolName: string, toolInput: unknown, result: {
|
|
51
62
|
content: string;
|
|
52
63
|
isError: boolean;
|
package/dist/hq/index.js
CHANGED
|
@@ -492,6 +492,9 @@ function isHqSubagentSummary(x) {
|
|
|
492
492
|
const v = x;
|
|
493
493
|
return typeof v.subagentId === "string" && typeof v.status === "string" && HQ_FLEET_SUBAGENT_STATUS.has(v.status);
|
|
494
494
|
}
|
|
495
|
+
function isOptionalFiniteNumber(x) {
|
|
496
|
+
return x === void 0 || typeof x === "number" && Number.isFinite(x);
|
|
497
|
+
}
|
|
495
498
|
function isHqFleetSnapshotPayload(x) {
|
|
496
499
|
if (typeof x !== "object" || x === null) return false;
|
|
497
500
|
const v = x;
|
|
@@ -501,6 +504,13 @@ function isHqFleetSnapshotPayload(x) {
|
|
|
501
504
|
for (const s of v.subagents) {
|
|
502
505
|
if (!isHqSubagentSummary(s)) return false;
|
|
503
506
|
}
|
|
507
|
+
if (!isOptionalFiniteNumber(v.maxConcurrent)) return false;
|
|
508
|
+
if (!isOptionalFiniteNumber(v.maxSpawns)) return false;
|
|
509
|
+
if (!isOptionalFiniteNumber(v.usedSpawns)) return false;
|
|
510
|
+
if (!isOptionalFiniteNumber(v.remainingSpawns)) return false;
|
|
511
|
+
if (!isOptionalFiniteNumber(v.checkpointMaxSpawns)) return false;
|
|
512
|
+
if (v.effectiveSource !== void 0 && typeof v.effectiveSource !== "string") return false;
|
|
513
|
+
if (v.ceilingMismatch !== void 0 && typeof v.ceilingMismatch !== "boolean") return false;
|
|
504
514
|
return true;
|
|
505
515
|
}
|
|
506
516
|
function isHqFleetEventPayload(x) {
|
|
@@ -1295,6 +1305,7 @@ function createMailboxEventPayload(input) {
|
|
|
1295
1305
|
var TRANSCRIPT_EVENT_TYPES = /* @__PURE__ */ new Set(["session.transcript", "agent.message"]);
|
|
1296
1306
|
var OPEN_STATE = 1;
|
|
1297
1307
|
var CONNECT_WARN_AFTER_FAILURES = 5;
|
|
1308
|
+
var DEFAULT_CONNECT_WARN_COOLDOWN_MS = 5 * 6e4;
|
|
1298
1309
|
var DEFAULT_RECONNECT_BASE_MS = 1e3;
|
|
1299
1310
|
var DEFAULT_RECONNECT_MAX_MS = 3e4;
|
|
1300
1311
|
var DEFAULT_DISCOVERY_POLL_MS = 5e3;
|
|
@@ -1306,6 +1317,7 @@ var DEFAULT_MAX_QUEUED_BYTES = Math.min(
|
|
|
1306
1317
|
var DEFAULT_COMMAND_POLL_INTERVAL_MS = 500;
|
|
1307
1318
|
var DEFAULT_COMMAND_POLL_LIMIT = 25;
|
|
1308
1319
|
var DEFAULT_HEARTBEAT_INTERVAL_MS = 25e3;
|
|
1320
|
+
var lastConnectWarningAtMs = 0;
|
|
1309
1321
|
function queuedFrameCoalesceKey(frame) {
|
|
1310
1322
|
if (frame.type !== "client.event" || !frame.event.type.endsWith(".snapshot")) {
|
|
1311
1323
|
return void 0;
|
|
@@ -1357,6 +1369,8 @@ var HqPublisher = class {
|
|
|
1357
1369
|
this.reconnect = options.reconnect ?? true;
|
|
1358
1370
|
this.reconnectBaseMs = options.reconnectBaseMs ?? DEFAULT_RECONNECT_BASE_MS;
|
|
1359
1371
|
this.reconnectMaxMs = options.reconnectMaxMs ?? DEFAULT_RECONNECT_MAX_MS;
|
|
1372
|
+
this.connectWarnAfterFailures = options.connectWarnAfterFailures ?? CONNECT_WARN_AFTER_FAILURES;
|
|
1373
|
+
this.connectWarnCooldownMs = options.connectWarnCooldownMs ?? DEFAULT_CONNECT_WARN_COOLDOWN_MS;
|
|
1360
1374
|
this.maxQueuedMessages = options.maxQueuedMessages ?? DEFAULT_MAX_QUEUED_MESSAGES;
|
|
1361
1375
|
this.maxQueuedBytes = options.maxQueuedBytes ?? DEFAULT_MAX_QUEUED_BYTES;
|
|
1362
1376
|
this.resolvedRedactionPolicy = resolveHqRedactionPolicy(options.redactionPolicy);
|
|
@@ -1370,6 +1384,8 @@ var HqPublisher = class {
|
|
|
1370
1384
|
reconnect;
|
|
1371
1385
|
reconnectBaseMs;
|
|
1372
1386
|
reconnectMaxMs;
|
|
1387
|
+
connectWarnAfterFailures;
|
|
1388
|
+
connectWarnCooldownMs;
|
|
1373
1389
|
maxQueuedMessages;
|
|
1374
1390
|
maxQueuedBytes;
|
|
1375
1391
|
resolvedRedactionPolicy;
|
|
@@ -1781,9 +1797,8 @@ var HqPublisher = class {
|
|
|
1781
1797
|
if (this.stopped || !this.reconnect || this.reconnectTimer !== null) return;
|
|
1782
1798
|
const delay = Math.min(this.reconnectMaxMs, this.reconnectBaseMs * 2 ** this.reconnectAttempt);
|
|
1783
1799
|
this.reconnectAttempt += 1;
|
|
1784
|
-
if (!this.connectWarningEmitted && this.reconnectAttempt >=
|
|
1785
|
-
this.connectWarningEmitted = true;
|
|
1786
|
-
this.emitConnectWarning();
|
|
1800
|
+
if (!this.connectWarningEmitted && this.reconnectAttempt >= this.connectWarnAfterFailures) {
|
|
1801
|
+
if (this.emitConnectWarning()) this.connectWarningEmitted = true;
|
|
1787
1802
|
}
|
|
1788
1803
|
this.reconnectTimer = setTimeout(() => {
|
|
1789
1804
|
this.reconnectTimer = null;
|
|
@@ -1792,11 +1807,16 @@ var HqPublisher = class {
|
|
|
1792
1807
|
this.reconnectTimer.unref?.();
|
|
1793
1808
|
}
|
|
1794
1809
|
emitConnectWarning() {
|
|
1810
|
+
const nowMs = Date.now();
|
|
1811
|
+
if (nowMs - lastConnectWarningAtMs < this.connectWarnCooldownMs) {
|
|
1812
|
+
return false;
|
|
1813
|
+
}
|
|
1814
|
+
lastConnectWarningAtMs = nowMs;
|
|
1795
1815
|
const attempt = this.lastAttempt;
|
|
1796
1816
|
const message = `WrongStack HQ publisher: ${this.reconnectAttempt} consecutive connection failures${attempt !== null ? ` to ${attempt.url} (client token ${attempt.hadToken ? "present" : "absent"})` : ""}. Either the HQ server is unreachable or it rejected the token (401). If HQ runs in client-token mode, verify WRONGSTACK_HQ_TOKEN / auth.json. Retries continue with backoff.`;
|
|
1797
1817
|
if (this.logger) {
|
|
1798
1818
|
this.logger.warn(message, { event: "hq.publisher.connect_failed" });
|
|
1799
|
-
return;
|
|
1819
|
+
return true;
|
|
1800
1820
|
}
|
|
1801
1821
|
const warn = this.options.warn ?? ((msg) => console.warn(
|
|
1802
1822
|
JSON.stringify({
|
|
@@ -1810,6 +1830,7 @@ var HqPublisher = class {
|
|
|
1810
1830
|
warn(message);
|
|
1811
1831
|
} catch {
|
|
1812
1832
|
}
|
|
1833
|
+
return true;
|
|
1813
1834
|
}
|
|
1814
1835
|
/**
|
|
1815
1836
|
* Dormant re-check while no HQ endpoint is discoverable. Uses a FIXED
|
|
@@ -2061,6 +2082,20 @@ function windowsAccountName() {
|
|
|
2061
2082
|
return username;
|
|
2062
2083
|
}
|
|
2063
2084
|
|
|
2085
|
+
// src/utils/pid.ts
|
|
2086
|
+
function isPidAlive(pid) {
|
|
2087
|
+
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
2088
|
+
if (pid === process.pid) return true;
|
|
2089
|
+
try {
|
|
2090
|
+
process.kill(pid, 0);
|
|
2091
|
+
return true;
|
|
2092
|
+
} catch (err) {
|
|
2093
|
+
const code = err.code;
|
|
2094
|
+
if (code === "EPERM") return true;
|
|
2095
|
+
return false;
|
|
2096
|
+
}
|
|
2097
|
+
}
|
|
2098
|
+
|
|
2064
2099
|
// src/utils/wstack-paths.ts
|
|
2065
2100
|
import { createHash } from "node:crypto";
|
|
2066
2101
|
import * as fs2 from "node:fs";
|
|
@@ -2078,6 +2113,9 @@ function canonicalProjectRoot(absRoot) {
|
|
|
2078
2113
|
const commonDirFile = path2.join(gitDir, "commondir");
|
|
2079
2114
|
if (!fs2.statSync(commonDirFile).isFile()) return checkoutRoot;
|
|
2080
2115
|
const commonDir = path2.resolve(gitDir, fs2.readFileSync(commonDirFile, "utf8").trim());
|
|
2116
|
+
const worktreesDir = path2.dirname(gitDir);
|
|
2117
|
+
if (path2.basename(worktreesDir).toLowerCase() !== "worktrees") return checkoutRoot;
|
|
2118
|
+
if (path2.resolve(worktreesDir, "..") !== commonDir) return checkoutRoot;
|
|
2081
2119
|
if (path2.basename(commonDir).toLowerCase() !== ".git") return checkoutRoot;
|
|
2082
2120
|
return path2.dirname(commonDir);
|
|
2083
2121
|
} catch {
|
|
@@ -2100,7 +2138,7 @@ function safeProfileName(name) {
|
|
|
2100
2138
|
const safe = (name ?? "").replace(/[/\\:]/g, "_").replace(/\.\./g, "_").trim();
|
|
2101
2139
|
return safe || "default";
|
|
2102
2140
|
}
|
|
2103
|
-
function
|
|
2141
|
+
function bootstrapProfileName(globalRoot) {
|
|
2104
2142
|
try {
|
|
2105
2143
|
const parsed = JSON.parse(fs2.readFileSync(path2.join(globalRoot, "config.json"), "utf8"));
|
|
2106
2144
|
return safeProfileName(
|
|
@@ -2118,7 +2156,7 @@ function wstackGlobalRoot() {
|
|
|
2118
2156
|
function resolveWstackPaths(opts) {
|
|
2119
2157
|
const globalRoot = opts.globalRoot ?? (opts.userHome ? path2.join(opts.userHome, ".wrongstack") : wstackGlobalRoot());
|
|
2120
2158
|
const homeDir = opts.userHome ?? os.homedir();
|
|
2121
|
-
const profileName = safeProfileName(opts.profileName ??
|
|
2159
|
+
const profileName = safeProfileName(opts.profileName ?? bootstrapProfileName(globalRoot));
|
|
2122
2160
|
const profileDir = path2.join(globalRoot, "profiles", profileName);
|
|
2123
2161
|
const hash = projectHash(opts.projectRoot);
|
|
2124
2162
|
const slug = projectSlug(opts.projectRoot);
|
|
@@ -2188,7 +2226,12 @@ function resolveWstackPaths(opts) {
|
|
|
2188
2226
|
projectRequirementIntakes: path2.join(projectDir, "requirement-intakes"),
|
|
2189
2227
|
syncConfig: path2.join(profileDir, "sync.json"),
|
|
2190
2228
|
configHistoryDir: path2.join(globalRoot, "config-history"),
|
|
2191
|
-
projectStatus: (
|
|
2229
|
+
projectStatus: (statusProjectSlug) => {
|
|
2230
|
+
if (!/^[a-z0-9](?:[a-z0-9-]{0,39})-[a-f0-9]{6}$/.test(statusProjectSlug)) {
|
|
2231
|
+
throw new Error(`Invalid project slug: ${statusProjectSlug}`);
|
|
2232
|
+
}
|
|
2233
|
+
return path2.join(globalRoot, "projects", statusProjectSlug, "status.json");
|
|
2234
|
+
}
|
|
2192
2235
|
};
|
|
2193
2236
|
}
|
|
2194
2237
|
|
|
@@ -2319,22 +2362,13 @@ async function writeHqRuntimeFile(dataDir, file) {
|
|
|
2319
2362
|
`, { mode: SECRET_FILE_MODE });
|
|
2320
2363
|
await restrictFilePermissions(target, { label: "hq-runtime" });
|
|
2321
2364
|
}
|
|
2322
|
-
function isProcessAlive(pid) {
|
|
2323
|
-
if (!Number.isInteger(pid) || pid <= 0) return false;
|
|
2324
|
-
try {
|
|
2325
|
-
process.kill(pid, 0);
|
|
2326
|
-
return true;
|
|
2327
|
-
} catch {
|
|
2328
|
-
return false;
|
|
2329
|
-
}
|
|
2330
|
-
}
|
|
2331
2365
|
function readHqRuntimeFileSync(dataDir) {
|
|
2332
2366
|
try {
|
|
2333
2367
|
const parsed = JSON.parse(
|
|
2334
2368
|
syncFs.readFileSync(hqRuntimeFilePath(dataDir), "utf8")
|
|
2335
2369
|
);
|
|
2336
2370
|
if (typeof parsed.url !== "string" || parsed.url.trim().length === 0) return void 0;
|
|
2337
|
-
if (typeof parsed.pid === "number" && !
|
|
2371
|
+
if (typeof parsed.pid === "number" && !isPidAlive(parsed.pid)) return void 0;
|
|
2338
2372
|
return {
|
|
2339
2373
|
url: parsed.url,
|
|
2340
2374
|
updatedAt: typeof parsed.updatedAt === "string" ? parsed.updatedAt : "",
|
|
@@ -2574,9 +2608,6 @@ function watchHqAuthFile(dataDir, onChange, opts = {}) {
|
|
|
2574
2608
|
}
|
|
2575
2609
|
}
|
|
2576
2610
|
});
|
|
2577
|
-
watcher.on("error", (err) => {
|
|
2578
|
-
opts.warn?.(`HQ auth watcher error: ${err.message}`);
|
|
2579
|
-
});
|
|
2580
2611
|
return {
|
|
2581
2612
|
close: () => {
|
|
2582
2613
|
closed = true;
|
|
@@ -3313,7 +3344,14 @@ function startFleetTelemetryBridge(opts) {
|
|
|
3313
3344
|
completedTasks: stats.completed,
|
|
3314
3345
|
failedTasks: stats.stopped,
|
|
3315
3346
|
...stats.totalCostUsd !== void 0 ? { totalCostUsd: stats.totalCostUsd } : {},
|
|
3316
|
-
subagents
|
|
3347
|
+
subagents,
|
|
3348
|
+
...stats.maxConcurrent !== void 0 ? { maxConcurrent: stats.maxConcurrent } : {},
|
|
3349
|
+
...stats.maxSpawns !== void 0 ? { maxSpawns: stats.maxSpawns } : {},
|
|
3350
|
+
...stats.usedSpawns !== void 0 ? { usedSpawns: stats.usedSpawns } : {},
|
|
3351
|
+
...stats.remainingSpawns !== void 0 ? { remainingSpawns: stats.remainingSpawns } : {},
|
|
3352
|
+
...stats.effectiveSource !== void 0 ? { effectiveSource: stats.effectiveSource } : {},
|
|
3353
|
+
...stats.checkpointMaxSpawns !== void 0 ? { checkpointMaxSpawns: stats.checkpointMaxSpawns } : {},
|
|
3354
|
+
...stats.ceilingMismatch ? { ceilingMismatch: true } : {}
|
|
3317
3355
|
};
|
|
3318
3356
|
}
|
|
3319
3357
|
const off = events.on("coordinator.stats", (stats) => {
|
|
@@ -7,6 +7,19 @@ export interface HqFleetSnapshotPayload {
|
|
|
7
7
|
failedTasks: number;
|
|
8
8
|
totalCostUsd?: number;
|
|
9
9
|
subagents: readonly HqSubagentSummary[];
|
|
10
|
+
/** Concurrent-subagent ceiling (maxConcurrent). */
|
|
11
|
+
maxConcurrent?: number | undefined;
|
|
12
|
+
/** Lifetime spawn ceiling (maxSpawns). */
|
|
13
|
+
maxSpawns?: number | undefined;
|
|
14
|
+
/** Cumulative spawns consumed this director run. */
|
|
15
|
+
usedSpawns?: number | undefined;
|
|
16
|
+
/** maxSpawns − usedSpawns (clamped ≥ 0). */
|
|
17
|
+
remainingSpawns?: number | undefined;
|
|
18
|
+
/** Winning config source labels, e.g. "maxConcurrent=profile, maxSpawns=env". */
|
|
19
|
+
effectiveSource?: string | undefined;
|
|
20
|
+
/** Historical checkpoint maxSpawns when it disagreed with the live ceiling. */
|
|
21
|
+
checkpointMaxSpawns?: number | undefined;
|
|
22
|
+
ceilingMismatch?: boolean | undefined;
|
|
10
23
|
}
|
|
11
24
|
export interface HqFleetEventPayload {
|
|
12
25
|
runId: string;
|
|
@@ -62,6 +75,13 @@ export interface HqFleetSummary {
|
|
|
62
75
|
failedTasks: number;
|
|
63
76
|
totalCostUsd?: number;
|
|
64
77
|
lastActivityAt: string;
|
|
78
|
+
maxConcurrent?: number | undefined;
|
|
79
|
+
maxSpawns?: number | undefined;
|
|
80
|
+
usedSpawns?: number | undefined;
|
|
81
|
+
remainingSpawns?: number | undefined;
|
|
82
|
+
effectiveSource?: string | undefined;
|
|
83
|
+
checkpointMaxSpawns?: number | undefined;
|
|
84
|
+
ceilingMismatch?: boolean | undefined;
|
|
65
85
|
}
|
|
66
86
|
export interface HqQueuedCommand {
|
|
67
87
|
commandId: string;
|
package/dist/hq/protocol.js
CHANGED
|
@@ -492,6 +492,9 @@ function isHqSubagentSummary(x) {
|
|
|
492
492
|
const v = x;
|
|
493
493
|
return typeof v.subagentId === "string" && typeof v.status === "string" && HQ_FLEET_SUBAGENT_STATUS.has(v.status);
|
|
494
494
|
}
|
|
495
|
+
function isOptionalFiniteNumber(x) {
|
|
496
|
+
return x === void 0 || typeof x === "number" && Number.isFinite(x);
|
|
497
|
+
}
|
|
495
498
|
function isHqFleetSnapshotPayload(x) {
|
|
496
499
|
if (typeof x !== "object" || x === null) return false;
|
|
497
500
|
const v = x;
|
|
@@ -501,6 +504,13 @@ function isHqFleetSnapshotPayload(x) {
|
|
|
501
504
|
for (const s of v.subagents) {
|
|
502
505
|
if (!isHqSubagentSummary(s)) return false;
|
|
503
506
|
}
|
|
507
|
+
if (!isOptionalFiniteNumber(v.maxConcurrent)) return false;
|
|
508
|
+
if (!isOptionalFiniteNumber(v.maxSpawns)) return false;
|
|
509
|
+
if (!isOptionalFiniteNumber(v.usedSpawns)) return false;
|
|
510
|
+
if (!isOptionalFiniteNumber(v.remainingSpawns)) return false;
|
|
511
|
+
if (!isOptionalFiniteNumber(v.checkpointMaxSpawns)) return false;
|
|
512
|
+
if (v.effectiveSource !== void 0 && typeof v.effectiveSource !== "string") return false;
|
|
513
|
+
if (v.ceilingMismatch !== void 0 && typeof v.ceilingMismatch !== "boolean") return false;
|
|
504
514
|
return true;
|
|
505
515
|
}
|
|
506
516
|
function isHqFleetEventPayload(x) {
|
package/dist/hq/publisher.d.ts
CHANGED
|
@@ -32,6 +32,20 @@ export interface HqPublisherOptions {
|
|
|
32
32
|
reconnect?: boolean;
|
|
33
33
|
reconnectBaseMs?: number;
|
|
34
34
|
reconnectMaxMs?: number;
|
|
35
|
+
/**
|
|
36
|
+
* Consecutive connect failures (never reaching `open`) before the one-time
|
|
37
|
+
* diagnostic warning fires. Defaults to `CONNECT_WARN_AFTER_FAILURES` (5).
|
|
38
|
+
*/
|
|
39
|
+
connectWarnAfterFailures?: number;
|
|
40
|
+
/**
|
|
41
|
+
* Minimum gap between connect-failure warnings ACROSS all HqPublisher
|
|
42
|
+
* instances in this process. When a fleet of agents fails against the same
|
|
43
|
+
* dead/rejecting HQ at once, only the first instance to cross the threshold
|
|
44
|
+
* warns; the rest stay silent until the window elapses (each instance still
|
|
45
|
+
* warns at most once). Defaults to 5 minutes. Set to 0 to disable the
|
|
46
|
+
* process-wide gate entirely.
|
|
47
|
+
*/
|
|
48
|
+
connectWarnCooldownMs?: number;
|
|
35
49
|
maxQueuedMessages?: number;
|
|
36
50
|
/** Hard byte cap on the outbound queue (prevents RAM blow-up when HQ is
|
|
37
51
|
* offline). Older frames are dropped oldest-first when exceeded. The default
|
|
@@ -66,11 +80,13 @@ export interface HqPublisherOptions {
|
|
|
66
80
|
*/
|
|
67
81
|
heartbeatIntervalMs?: number;
|
|
68
82
|
/**
|
|
69
|
-
* Diagnostic sink for the
|
|
70
|
-
*
|
|
83
|
+
* Diagnostic sink for the consecutive-connect-failure warning. The
|
|
84
|
+
* publisher is otherwise deliberately silent (dormant queueing), which
|
|
71
85
|
* made auth failures invisible: a token the server rejects — e.g. a wrong
|
|
72
86
|
* `WRONGSTACK_HQ_TOKEN` against a remote HQ — looked exactly like "HQ not
|
|
73
|
-
* running".
|
|
87
|
+
* running". Emission is one-shot per instance and rate-limited process-wide
|
|
88
|
+
* via {@link HqPublisherOptions.connectWarnCooldownMs}. Defaults to a
|
|
89
|
+
* single structured `console.warn` line.
|
|
74
90
|
*/
|
|
75
91
|
warn?: (message: string) => void;
|
|
76
92
|
/** Logger for structured events. When provided, the `warn` callback is derived from it. */
|
|
@@ -99,6 +115,8 @@ export declare class HqPublisher {
|
|
|
99
115
|
private readonly reconnect;
|
|
100
116
|
private readonly reconnectBaseMs;
|
|
101
117
|
private readonly reconnectMaxMs;
|
|
118
|
+
private readonly connectWarnAfterFailures;
|
|
119
|
+
private readonly connectWarnCooldownMs;
|
|
102
120
|
private readonly maxQueuedMessages;
|
|
103
121
|
private readonly maxQueuedBytes;
|
|
104
122
|
private readonly resolvedRedactionPolicy;
|