@indigoai-us/hq-cli 5.192.1 → 5.193.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/CHANGELOG.md +21 -0
- package/dist/commands/lanes.d.ts +2 -2
- package/dist/commands/lanes.js +40 -7
- package/dist/commands/secrets.js +16 -5
- package/dist/lib/company-skill-wrapper.d.ts +9 -0
- package/dist/lib/company-skill-wrapper.js +121 -29
- package/dist/sentry.js +5 -0
- package/dist/utils/contribution-table.d.ts +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,27 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [5.193.0] — 2026-09-25
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
|
|
9
|
+
- `hq secrets set` now handles a closed stdin descriptor during terminal cleanup.
|
|
10
|
+
|
|
11
|
+
### Improved
|
|
12
|
+
|
|
13
|
+
- Sentry reports for failed skill-wrapper replacement now include bounded ownership-state tags.
|
|
14
|
+
|
|
15
|
+
## [5.192.2] — 2026-09-25
|
|
16
|
+
|
|
17
|
+
### Fixed
|
|
18
|
+
|
|
19
|
+
- Create, resume, automatic repair, and apply now read
|
|
20
|
+
`lanes.claude-account-balance` from hq-flags. `HQ_FLAG_*` remains the local
|
|
21
|
+
override, and a registry read failure keeps the gate off. The
|
|
22
|
+
`accounts balance --json` report includes `balance_gate_enabled`.
|
|
23
|
+
- `hq lanes accounts balance --apply` now honors each lane's company-scoped
|
|
24
|
+
gate, skips disabled company lanes, and rechecks the gate before moving.
|
|
25
|
+
|
|
5
26
|
## [5.192.1] — 2026-09-25
|
|
6
27
|
|
|
7
28
|
### Fixed
|
package/dist/commands/lanes.d.ts
CHANGED
|
@@ -86,8 +86,8 @@ export interface LanesCommandDeps {
|
|
|
86
86
|
isSsmOwnerDispatch?: () => boolean;
|
|
87
87
|
/** Test seam for changing a lane between pre-lock and locked grant reads. */
|
|
88
88
|
withGrantHireLocks?: typeof withLaneHireLocks;
|
|
89
|
-
/** Test seam for the
|
|
90
|
-
laneClaudeAccountBalanceEnabled?: () => boolean
|
|
89
|
+
/** Test seam for the Claude account-balancing gate. */
|
|
90
|
+
laneClaudeAccountBalanceEnabled?: (company?: LaneCompanyRef | null) => boolean | Promise<boolean>;
|
|
91
91
|
}
|
|
92
92
|
export declare const LANE_OUTPOST_LOAD_BALANCING_FLAG = "lanes.outpost-load-balancing";
|
|
93
93
|
export declare function parseSeniorRef(raw: string): SeniorRef;
|
package/dist/commands/lanes.js
CHANGED
|
@@ -57,8 +57,10 @@ export const LANE_OUTPOST_LOAD_BALANCING_FLAG = "lanes.outpost-load-balancing";
|
|
|
57
57
|
function defaultLanePlacementEnabled() {
|
|
58
58
|
return resolveProcessFlagGate(LANE_OUTPOST_LOAD_BALANCING_FLAG, { fallback: false }, () => false);
|
|
59
59
|
}
|
|
60
|
-
function defaultLaneClaudeAccountBalanceEnabled() {
|
|
61
|
-
|
|
60
|
+
function defaultLaneClaudeAccountBalanceEnabled(company) {
|
|
61
|
+
// Keep the local HQ_FLAG override and the registry reader's fail-closed
|
|
62
|
+
// behavior, matching the lane hire-grants gate.
|
|
63
|
+
return readLanesRegistryFlag(CLAUDE_ACCOUNT_BALANCE_FLAG, { company });
|
|
62
64
|
}
|
|
63
65
|
function defaultLaneHireGrantsEnabled(company) {
|
|
64
66
|
return readLanesRegistryFlag(LANE_HIRE_GRANTS_FLAG, { company });
|
|
@@ -2516,7 +2518,7 @@ async function runCreate(ctx, opts) {
|
|
|
2516
2518
|
}
|
|
2517
2519
|
const allowedAccounts = validatedAccounts.size > 0 ? [...validatedAccounts] : profileAccounts;
|
|
2518
2520
|
let accountSelection;
|
|
2519
|
-
if (provider === "claude" && ctx.laneClaudeAccountBalanceEnabled()) {
|
|
2521
|
+
if (provider === "claude" && await ctx.laneClaudeAccountBalanceEnabled(company)) {
|
|
2520
2522
|
const targetHost = placementHostOverride ?? (outpostId !== undefined
|
|
2521
2523
|
? { kind: "outpost", id: outpostId }
|
|
2522
2524
|
: laneHostFromCtx(ctx));
|
|
@@ -4841,7 +4843,7 @@ async function prepareAccountMove(ctx, lane, input) {
|
|
|
4841
4843
|
async function selectAutomaticClaudeResumeAccount(ctx, lane) {
|
|
4842
4844
|
if (lane.provider !== "claude")
|
|
4843
4845
|
return undefined;
|
|
4844
|
-
if (!ctx.laneClaudeAccountBalanceEnabled()) {
|
|
4846
|
+
if (!await ctx.laneClaudeAccountBalanceEnabled(lane.company)) {
|
|
4845
4847
|
if (lane.account === undefined)
|
|
4846
4848
|
return undefined;
|
|
4847
4849
|
const config = loadLanesConfig(ctx.hqRoot());
|
|
@@ -5245,6 +5247,7 @@ async function applyClaudeBalanceMove(ctx, suggestion) {
|
|
|
5245
5247
|
return { lane_id: suggestion.lane_id, host: suggestion.host, from: suggestion.from, to: suggestion.to, state: suggestion.state };
|
|
5246
5248
|
}
|
|
5247
5249
|
const lane = laneWithRecoveredProviderSession(ctx.hqRoot(), readLane(ctx.hqRoot(), suggestion.lane_id));
|
|
5250
|
+
assertBalanceApplyEnabled(await ctx.laneClaudeAccountBalanceEnabled(lane.company));
|
|
5248
5251
|
if (lane.state === "running") {
|
|
5249
5252
|
throw new LanesError("lane_running", `Lane ${lane.lane_id} is running; balance never moves a running turn`, {
|
|
5250
5253
|
lane_id: lane.lane_id,
|
|
@@ -5273,8 +5276,8 @@ async function applyClaudeBalanceMove(ctx, suggestion) {
|
|
|
5273
5276
|
return { lane_id: moved.lane.lane_id, host: moved.lane.host, from: moved.from ?? suggestion.from, to: moved.to, state: moved.lane.state };
|
|
5274
5277
|
}
|
|
5275
5278
|
async function runClaudeBalanceMoveTarget(ctx, laneId, opts) {
|
|
5276
|
-
assertBalanceApplyEnabled(ctx.laneClaudeAccountBalanceEnabled());
|
|
5277
5279
|
const lane = laneWithRecoveredProviderSession(ctx.hqRoot(), readLane(ctx.hqRoot(), laneId));
|
|
5280
|
+
assertBalanceApplyEnabled(await ctx.laneClaudeAccountBalanceEnabled(lane.company));
|
|
5278
5281
|
assertLaneRunsHereForOperatorMutation(ctx, lane, "balance-move-target");
|
|
5279
5282
|
if (lane.provider !== "claude") {
|
|
5280
5283
|
throw new LanesError("claude_account_balance_provider_mismatch", `Lane ${laneId} does not use Claude`, {
|
|
@@ -6873,11 +6876,30 @@ async function runAccountsBalance(ctx, opts) {
|
|
|
6873
6876
|
accepted: ["claude"],
|
|
6874
6877
|
});
|
|
6875
6878
|
}
|
|
6876
|
-
if (opts.apply)
|
|
6877
|
-
assertBalanceApplyEnabled(ctx.laneClaudeAccountBalanceEnabled());
|
|
6878
6879
|
const hqRoot = ctx.hqRoot();
|
|
6879
6880
|
const config = loadLanesConfig(hqRoot);
|
|
6880
6881
|
const loaded = await loadClaudeBalanceHostSnapshots(ctx, hqRoot, config);
|
|
6882
|
+
const laneBalanceGateEnabled = new Map();
|
|
6883
|
+
const companyBalanceGateEnabled = new Map();
|
|
6884
|
+
const lanes = loaded.snapshots.flatMap((host) => host.lanes);
|
|
6885
|
+
for (const lane of lanes) {
|
|
6886
|
+
const companyKey = JSON.stringify([lane.company?.uid ?? null, lane.company?.slug ?? null]);
|
|
6887
|
+
let enabled = companyBalanceGateEnabled.get(companyKey);
|
|
6888
|
+
if (enabled === undefined) {
|
|
6889
|
+
enabled = await ctx.laneClaudeAccountBalanceEnabled(lane.company);
|
|
6890
|
+
companyBalanceGateEnabled.set(companyKey, enabled);
|
|
6891
|
+
}
|
|
6892
|
+
laneBalanceGateEnabled.set(lane.lane_id, enabled);
|
|
6893
|
+
}
|
|
6894
|
+
// With no lanes there is no tenant scope to resolve, so report and enforce
|
|
6895
|
+
// the unscoped/default gate. Otherwise the report reflects whether any
|
|
6896
|
+
// represented lane company has the gate enabled.
|
|
6897
|
+
if (lanes.length === 0) {
|
|
6898
|
+
companyBalanceGateEnabled.set(JSON.stringify([null, null]), await ctx.laneClaudeAccountBalanceEnabled());
|
|
6899
|
+
}
|
|
6900
|
+
const balanceGateEnabled = [...companyBalanceGateEnabled.values()].some(Boolean);
|
|
6901
|
+
if (opts.apply)
|
|
6902
|
+
assertBalanceApplyEnabled(balanceGateEnabled);
|
|
6881
6903
|
const loads = accountLoadsForReport({ hosts: loaded.snapshots, config });
|
|
6882
6904
|
const suggestions = planClaudeBalanceMoves({ hosts: loaded.snapshots, config });
|
|
6883
6905
|
const selections = loaded.snapshots.map((host) => {
|
|
@@ -6939,6 +6961,16 @@ async function runAccountsBalance(ctx, opts) {
|
|
|
6939
6961
|
});
|
|
6940
6962
|
continue;
|
|
6941
6963
|
}
|
|
6964
|
+
if (laneBalanceGateEnabled.get(suggestion.lane_id) !== true) {
|
|
6965
|
+
skipped.push({
|
|
6966
|
+
lane_id: suggestion.lane_id,
|
|
6967
|
+
host: suggestion.host,
|
|
6968
|
+
from: suggestion.from,
|
|
6969
|
+
to: suggestion.to,
|
|
6970
|
+
reason: "claude_account_balance_disabled",
|
|
6971
|
+
});
|
|
6972
|
+
continue;
|
|
6973
|
+
}
|
|
6942
6974
|
try {
|
|
6943
6975
|
moved.push(await applyClaudeBalanceMove(ctx, suggestion));
|
|
6944
6976
|
}
|
|
@@ -6957,6 +6989,7 @@ async function runAccountsBalance(ctx, opts) {
|
|
|
6957
6989
|
ok: true,
|
|
6958
6990
|
action: "accounts.balance",
|
|
6959
6991
|
provider: "claude",
|
|
6992
|
+
balance_gate_enabled: balanceGateEnabled,
|
|
6960
6993
|
accounts,
|
|
6961
6994
|
selections,
|
|
6962
6995
|
unavailable_hosts: loaded.unavailableHosts,
|
package/dist/commands/secrets.js
CHANGED
|
@@ -188,12 +188,23 @@ function promptSecretInteractively() {
|
|
|
188
188
|
let value = "";
|
|
189
189
|
process.stdin.setEncoding("utf8");
|
|
190
190
|
const cleanup = () => {
|
|
191
|
-
|
|
192
|
-
process.stdin.
|
|
191
|
+
try {
|
|
192
|
+
if (process.stdin.isTTY) {
|
|
193
|
+
try {
|
|
194
|
+
process.stdin.setRawMode(false);
|
|
195
|
+
}
|
|
196
|
+
catch (error) {
|
|
197
|
+
if (error?.code !== "EBADF") {
|
|
198
|
+
throw error;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
finally {
|
|
204
|
+
process.stdin.removeListener("data", onData);
|
|
205
|
+
process.stdin.removeListener("end", onEnd);
|
|
206
|
+
process.stdin.pause();
|
|
193
207
|
}
|
|
194
|
-
process.stdin.removeListener("data", onData);
|
|
195
|
-
process.stdin.removeListener("end", onEnd);
|
|
196
|
-
process.stdin.pause();
|
|
197
208
|
};
|
|
198
209
|
const onEnd = () => {
|
|
199
210
|
cleanup();
|
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
import * as fs from "node:fs";
|
|
2
|
+
export interface CompanySkillWrapperOwnershipDiagnostics {
|
|
3
|
+
kind: "directory" | "symlink" | "file" | "other";
|
|
4
|
+
bridgeState: "all_live" | "partial" | "none" | "empty" | "not_applicable";
|
|
5
|
+
markerState: "bound" | "missing" | "invalid" | "unbound" | "unavailable" | "not_applicable";
|
|
6
|
+
linkTarget: "company_skills" | "outside_company_skills" | "unavailable" | "not_applicable";
|
|
7
|
+
platform: "win32" | "darwin" | "linux" | "other";
|
|
8
|
+
nameEncoding: "percent_encoded" | "literal";
|
|
9
|
+
}
|
|
10
|
+
export declare function companySkillWrapperSentryTagsFor(error: unknown): Record<string, string> | undefined;
|
|
2
11
|
export interface SurfaceCompanySkillInput {
|
|
3
12
|
hqRoot: string;
|
|
4
13
|
companySlug: string;
|
|
@@ -62,20 +62,6 @@ function symlinkTarget(linkPath) {
|
|
|
62
62
|
function generatedMarkerPath(wrapperPath) {
|
|
63
63
|
return path.join(path.dirname(wrapperPath), WRAPPER_MARKER_DIR, `${path.basename(wrapperPath)}.json`);
|
|
64
64
|
}
|
|
65
|
-
function hasBoundGeneratedMarker(wrapperPath, companySlug) {
|
|
66
|
-
try {
|
|
67
|
-
const marker = JSON.parse(fs.readFileSync(generatedMarkerPath(wrapperPath), "utf8"));
|
|
68
|
-
const wrapper = fs.lstatSync(wrapperPath);
|
|
69
|
-
return (marker.version === 2 &&
|
|
70
|
-
marker.companySlug === companySlug &&
|
|
71
|
-
marker.wrapperDev === wrapper.dev &&
|
|
72
|
-
marker.wrapperIno === wrapper.ino &&
|
|
73
|
-
marker.wrapperBirthtimeMs === wrapper.birthtimeMs);
|
|
74
|
-
}
|
|
75
|
-
catch {
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
65
|
function writeGeneratedMarker(wrapperPath, companySlug, skillSlug) {
|
|
80
66
|
const markerPath = generatedMarkerPath(wrapperPath);
|
|
81
67
|
try {
|
|
@@ -104,21 +90,116 @@ function removeGeneratedMarker(wrapperPath) {
|
|
|
104
90
|
// never sufficient to surface content on its own.
|
|
105
91
|
}
|
|
106
92
|
}
|
|
107
|
-
|
|
93
|
+
const COMPANY_SKILL_WRAPPER_DIAGNOSTICS = Symbol("company-skill-wrapper-diagnostics");
|
|
94
|
+
function companySkillWrapperOwnershipError(wrapperPath, diagnostics) {
|
|
95
|
+
const error = new Error(`Cannot replace '${wrapperPath}' because it is not an HQ-generated skill wrapper.`);
|
|
96
|
+
Object.defineProperty(error, COMPANY_SKILL_WRAPPER_DIAGNOSTICS, {
|
|
97
|
+
value: diagnostics,
|
|
98
|
+
});
|
|
99
|
+
return error;
|
|
100
|
+
}
|
|
101
|
+
export function companySkillWrapperSentryTagsFor(error) {
|
|
102
|
+
if (!(error instanceof Error))
|
|
103
|
+
return undefined;
|
|
104
|
+
const diagnostics = error[COMPANY_SKILL_WRAPPER_DIAGNOSTICS];
|
|
105
|
+
if (!diagnostics)
|
|
106
|
+
return undefined;
|
|
107
|
+
return {
|
|
108
|
+
hq_cli_skill_wrapper_kind: diagnostics.kind,
|
|
109
|
+
hq_cli_skill_wrapper_bridge_state: diagnostics.bridgeState,
|
|
110
|
+
hq_cli_skill_wrapper_marker_state: diagnostics.markerState,
|
|
111
|
+
hq_cli_skill_wrapper_link_target: diagnostics.linkTarget,
|
|
112
|
+
hq_cli_skill_wrapper_platform: diagnostics.platform,
|
|
113
|
+
hq_cli_skill_wrapper_name_encoding: diagnostics.nameEncoding,
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
function inspectGeneratedNamespacedWrapper(wrapperPath, hqRoot, companySlug) {
|
|
108
117
|
const companySkillsRoot = path.join(hqRoot, "companies", companySlug, "skills");
|
|
109
|
-
const
|
|
110
|
-
if (
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
118
|
+
const wrapperEntry = lstat(wrapperPath);
|
|
119
|
+
if (wrapperEntry?.isSymbolicLink()) {
|
|
120
|
+
const target = symlinkTarget(wrapperPath);
|
|
121
|
+
const linkTarget = target === undefined
|
|
122
|
+
? "unavailable"
|
|
123
|
+
: isWithin(companySkillsRoot, target)
|
|
124
|
+
? "company_skills"
|
|
125
|
+
: "outside_company_skills";
|
|
126
|
+
return {
|
|
127
|
+
owned: linkTarget === "company_skills",
|
|
128
|
+
diagnostics: {
|
|
129
|
+
kind: "symlink",
|
|
130
|
+
bridgeState: "not_applicable",
|
|
131
|
+
markerState: "not_applicable",
|
|
132
|
+
linkTarget,
|
|
133
|
+
},
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
if (!wrapperEntry?.isDirectory()) {
|
|
137
|
+
return {
|
|
138
|
+
owned: false,
|
|
139
|
+
diagnostics: {
|
|
140
|
+
kind: wrapperEntry?.isFile() ? "file" : "other",
|
|
141
|
+
bridgeState: "not_applicable",
|
|
142
|
+
markerState: "not_applicable",
|
|
143
|
+
linkTarget: "not_applicable",
|
|
144
|
+
},
|
|
145
|
+
};
|
|
146
|
+
}
|
|
115
147
|
const entries = fs.readdirSync(wrapperPath).filter((entry) => !entry.startsWith("."));
|
|
116
|
-
const
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
148
|
+
const liveBridgeCount = entries.reduce((count, entry) => {
|
|
149
|
+
const target = symlinkTarget(path.join(wrapperPath, entry));
|
|
150
|
+
return count + (target !== undefined && isWithin(companySkillsRoot, target) ? 1 : 0);
|
|
151
|
+
}, 0);
|
|
152
|
+
const bridgeState = entries.length === 0
|
|
153
|
+
? "empty"
|
|
154
|
+
: liveBridgeCount === entries.length
|
|
155
|
+
? "all_live"
|
|
156
|
+
: liveBridgeCount > 0
|
|
157
|
+
? "partial"
|
|
158
|
+
: "none";
|
|
159
|
+
const markerState = generatedMarkerState(wrapperPath, companySlug);
|
|
160
|
+
return {
|
|
161
|
+
owned: bridgeState === "all_live" || markerState === "bound",
|
|
162
|
+
diagnostics: {
|
|
163
|
+
kind: "directory",
|
|
164
|
+
bridgeState,
|
|
165
|
+
markerState,
|
|
166
|
+
linkTarget: "not_applicable",
|
|
167
|
+
},
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
function isGeneratedNamespacedWrapper(wrapperPath, hqRoot, companySlug) {
|
|
171
|
+
return inspectGeneratedNamespacedWrapper(wrapperPath, hqRoot, companySlug).owned;
|
|
172
|
+
}
|
|
173
|
+
function generatedMarkerState(wrapperPath, companySlug) {
|
|
174
|
+
let markerContents;
|
|
175
|
+
try {
|
|
176
|
+
markerContents = fs.readFileSync(generatedMarkerPath(wrapperPath), "utf8");
|
|
177
|
+
}
|
|
178
|
+
catch (error) {
|
|
179
|
+
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT"
|
|
180
|
+
? "missing"
|
|
181
|
+
: "unavailable";
|
|
182
|
+
}
|
|
183
|
+
let marker;
|
|
184
|
+
try {
|
|
185
|
+
marker = JSON.parse(markerContents);
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
return "invalid";
|
|
189
|
+
}
|
|
190
|
+
try {
|
|
191
|
+
const wrapper = fs.lstatSync(wrapperPath);
|
|
192
|
+
return marker.version === 2 &&
|
|
193
|
+
marker.companySlug === companySlug &&
|
|
194
|
+
marker.wrapperDev === wrapper.dev &&
|
|
195
|
+
marker.wrapperIno === wrapper.ino &&
|
|
196
|
+
marker.wrapperBirthtimeMs === wrapper.birthtimeMs
|
|
197
|
+
? "bound"
|
|
198
|
+
: "unbound";
|
|
199
|
+
}
|
|
200
|
+
catch {
|
|
201
|
+
return "unavailable";
|
|
202
|
+
}
|
|
122
203
|
}
|
|
123
204
|
function isGeneratedLegacyEntry(legacyPath, sourceDir, skillFile) {
|
|
124
205
|
const target = symlinkTarget(legacyPath);
|
|
@@ -224,8 +305,19 @@ export function surfaceCompanySkill(input) {
|
|
|
224
305
|
const backupPath = `${stagingPath}.previous`;
|
|
225
306
|
let previousMoved = false;
|
|
226
307
|
if (currentWrapper) {
|
|
227
|
-
|
|
228
|
-
|
|
308
|
+
const ownership = inspectGeneratedNamespacedWrapper(wrapperPath, input.hqRoot, input.companySlug);
|
|
309
|
+
if (!ownership.owned) {
|
|
310
|
+
const runtimePlatform = process.platform;
|
|
311
|
+
const platform = win32
|
|
312
|
+
? "win32"
|
|
313
|
+
: runtimePlatform === "darwin" || runtimePlatform === "linux"
|
|
314
|
+
? runtimePlatform
|
|
315
|
+
: "other";
|
|
316
|
+
throw companySkillWrapperOwnershipError(wrapperPath, {
|
|
317
|
+
...ownership.diagnostics,
|
|
318
|
+
platform,
|
|
319
|
+
nameEncoding: win32 ? "percent_encoded" : "literal",
|
|
320
|
+
});
|
|
229
321
|
}
|
|
230
322
|
renameEntry(wrapperPath, backupPath);
|
|
231
323
|
previousMoved = true;
|
package/dist/sentry.js
CHANGED
|
@@ -5,6 +5,7 @@ import { beforeBreadcrumb } from "./utils/breadcrumb-buffer.js";
|
|
|
5
5
|
import { CLI_VERSION } from "./cli-version.js";
|
|
6
6
|
import { getCachedSentryUser } from "./utils/sentry-identity.js";
|
|
7
7
|
import { isEpipe } from "./utils/epipe.js";
|
|
8
|
+
import { companySkillWrapperSentryTagsFor } from "./lib/company-skill-wrapper.js";
|
|
8
9
|
import { environmentalFsErrorMessage } from "./utils/environmental-error.js";
|
|
9
10
|
import { incompleteInstallCaptureContext, incompleteInstallMessage, } from "./utils/incomplete-install-error.js";
|
|
10
11
|
import { installTreeTornSentryTagsFor, sentryFingerprintFor, sentryMcpCollisionContextFor, } from "./utils/sentry-fingerprint.js";
|
|
@@ -84,6 +85,10 @@ export function epipeAwareBeforeSend(event, hint) {
|
|
|
84
85
|
if (fingerprint)
|
|
85
86
|
event.fingerprint = fingerprint;
|
|
86
87
|
}
|
|
88
|
+
const skillWrapperTags = companySkillWrapperSentryTagsFor(hint?.originalException);
|
|
89
|
+
if (skillWrapperTags) {
|
|
90
|
+
event.tags = { ...event.tags, ...skillWrapperTags };
|
|
91
|
+
}
|
|
87
92
|
const installTreeTags = installTreeTornSentryTagsFor(hint?.originalException);
|
|
88
93
|
if (installTreeTags) {
|
|
89
94
|
event.tags = { ...event.tags, ...installTreeTags };
|
|
@@ -99,5 +99,5 @@ export declare const CONTRIBUTION_KEYS: ContributionKey[];
|
|
|
99
99
|
*/
|
|
100
100
|
export declare function payloadFor(key: ContributionKey, item: string): string;
|
|
101
101
|
/** Keys whose contributions are wired by a host symlink (skip `merge` rows). */
|
|
102
|
-
export declare const SYMLINK_KEYS: ("workers" | "mcp" | "
|
|
102
|
+
export declare const SYMLINK_KEYS: ("workers" | "mcp" | "skills" | "knowledge" | "commands" | "hooks" | "policies" | "scripts")[];
|
|
103
103
|
//# sourceMappingURL=contribution-table.d.ts.map
|