@companyhelm/runner 0.1.3 → 0.2.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/RUNTIME_IMAGE_VERSION +1 -1
- package/dist/commands/root.js +105 -272
- package/dist/provisioning/runtime_provisioning/script_renderer.js +0 -10
- package/dist/provisioning/runtime_provisioning/system_prompt.js +1 -0
- package/dist/service/companyhelm_api_client.js +0 -48
- package/dist/service/thread_lifecycle.js +0 -33
- package/dist/templates/system_prompts/common.md.j2 +5 -14
- package/dist/templates/system_prompts/shared_workspace.md.j2 +1 -0
- package/dist/testing/vitest_reporter.js +23 -0
- package/package.json +2 -2
- package/dist/templates/provisioning/runtime_agent_cli_config.sh.j2 +0 -8
package/RUNTIME_IMAGE_VERSION
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
0.0.
|
|
1
|
+
0.0.12
|
package/dist/commands/root.js
CHANGED
|
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.toErrorMessage = toErrorMessage;
|
|
37
|
+
exports.formatWorkspaceStartupMessage = formatWorkspaceStartupMessage;
|
|
37
38
|
exports.isRetryableApiConnectionError = isRetryableApiConnectionError;
|
|
38
39
|
exports.formatApiConnectionFailureMessage = formatApiConnectionFailureMessage;
|
|
39
40
|
exports.formatApiConnectionFailureDiagnostics = formatApiConnectionFailureDiagnostics;
|
|
@@ -42,6 +43,7 @@ exports.isNoActiveTurnSteerError = isNoActiveTurnSteerError;
|
|
|
42
43
|
exports.isNoRunningTurnInterruptError = isNoRunningTurnInterruptError;
|
|
43
44
|
exports.normalizeThreadAgentApiUrlForRuntime = normalizeThreadAgentApiUrlForRuntime;
|
|
44
45
|
exports.extractThreadNameUpdateFromNotification = extractThreadNameUpdateFromNotification;
|
|
46
|
+
exports.extractThreadTokenUsageUpdateFromNotification = extractThreadTokenUsageUpdateFromNotification;
|
|
45
47
|
exports.runCommandLoop = runCommandLoop;
|
|
46
48
|
exports.isInternalDaemonChildProcess = isInternalDaemonChildProcess;
|
|
47
49
|
exports.runDetachedDaemonProcess = runDetachedDaemonProcess;
|
|
@@ -83,14 +85,9 @@ const auth_js_1 = require("./sdk/codex/auth.js");
|
|
|
83
85
|
const COMMAND_CHANNEL_CONNECT_RETRY_DELAY_MS = 1000;
|
|
84
86
|
const COMMAND_CHANNEL_OPEN_TIMEOUT_MS = 5000;
|
|
85
87
|
const TURN_COMPLETION_TIMEOUT_MS = 2 * 60 * 60000;
|
|
86
|
-
const GITHUB_INSTALLATIONS_SYNC_INTERVAL_MS = 5 * 60000;
|
|
87
|
-
const GITHUB_INSTALLATIONS_MIN_SYNC_INTERVAL_MS = 30000;
|
|
88
|
-
const GITHUB_INSTALLATIONS_REFRESH_WINDOW_MS = 15 * 60000;
|
|
89
88
|
const WORKSPACE_INSTALLATIONS_DIRECTORY = ".companyhelm";
|
|
90
|
-
const WORKSPACE_INSTALLATIONS_FILENAME = "installations.json";
|
|
91
89
|
const THREAD_GIT_SKILLS_CONFIG_FILENAME = "thread-git-skills.json";
|
|
92
90
|
const THREAD_MCP_CONFIG_FILENAME = "thread-mcp.json";
|
|
93
|
-
const THREAD_AGENT_CLI_CONFIG_FILENAME = "thread-agent-cli.json";
|
|
94
91
|
const THREAD_MCP_BEARER_TOKEN_ENV_PREFIX = "COMPANYHELM_MCP_TOKEN_";
|
|
95
92
|
const THREAD_MCP_AUTH_TYPE_BEARER_TOKEN = 2;
|
|
96
93
|
const THREAD_MCP_STARTUP_TIMEOUT_SECONDS = 60;
|
|
@@ -262,6 +259,20 @@ function normalizeReasoningLevels(value) {
|
|
|
262
259
|
function toErrorMessage(error) {
|
|
263
260
|
return error instanceof Error ? error.message : String(error);
|
|
264
261
|
}
|
|
262
|
+
function formatWorkspaceStartupMessage(cfg) {
|
|
263
|
+
if (cfg.use_dedicated_workspaces) {
|
|
264
|
+
const workspacesDirectory = (0, thread_lifecycle_js_1.resolveThreadsRootDirectory)(cfg.config_directory, cfg.workspaces_directory);
|
|
265
|
+
return `Workspace modality: dedicated (workspaces dir: ${workspacesDirectory})`;
|
|
266
|
+
}
|
|
267
|
+
const workspaceDirectory = (0, thread_workspace_provisioner_js_1.resolveThreadWorkspaceDirectory)({
|
|
268
|
+
configDirectory: cfg.config_directory,
|
|
269
|
+
workspacesDirectory: cfg.workspaces_directory,
|
|
270
|
+
workspacePath: cfg.workspace_path,
|
|
271
|
+
useDedicatedWorkspaces: false,
|
|
272
|
+
threadId: "startup",
|
|
273
|
+
});
|
|
274
|
+
return `Workspace modality: shared (workspace: ${workspaceDirectory})`;
|
|
275
|
+
}
|
|
265
276
|
function getGrpcStatusCode(error) {
|
|
266
277
|
if (!error || typeof error !== "object" || !("code" in error)) {
|
|
267
278
|
return undefined;
|
|
@@ -362,6 +373,36 @@ function normalizeNonEmptyString(value) {
|
|
|
362
373
|
const trimmed = value.trim();
|
|
363
374
|
return trimmed.length > 0 ? trimmed : undefined;
|
|
364
375
|
}
|
|
376
|
+
function normalizeNonNegativeNumber(value) {
|
|
377
|
+
if (typeof value !== "number" || !Number.isFinite(value) || value < 0) {
|
|
378
|
+
return undefined;
|
|
379
|
+
}
|
|
380
|
+
return Math.floor(value);
|
|
381
|
+
}
|
|
382
|
+
function resolveTokenUsageBreakdown(value) {
|
|
383
|
+
if (!isRecord(value)) {
|
|
384
|
+
return null;
|
|
385
|
+
}
|
|
386
|
+
const inputTokens = normalizeNonNegativeNumber(value.inputTokens ?? value.input_tokens);
|
|
387
|
+
const cachedInputTokens = normalizeNonNegativeNumber(value.cachedInputTokens ?? value.cached_input_tokens);
|
|
388
|
+
const outputTokens = normalizeNonNegativeNumber(value.outputTokens ?? value.output_tokens);
|
|
389
|
+
const reasoningOutputTokens = normalizeNonNegativeNumber(value.reasoningOutputTokens ?? value.reasoning_output_tokens);
|
|
390
|
+
const totalTokens = normalizeNonNegativeNumber(value.totalTokens ?? value.total_tokens);
|
|
391
|
+
if (inputTokens === undefined ||
|
|
392
|
+
cachedInputTokens === undefined ||
|
|
393
|
+
outputTokens === undefined ||
|
|
394
|
+
reasoningOutputTokens === undefined ||
|
|
395
|
+
totalTokens === undefined) {
|
|
396
|
+
return null;
|
|
397
|
+
}
|
|
398
|
+
return {
|
|
399
|
+
inputTokens,
|
|
400
|
+
cachedInputTokens,
|
|
401
|
+
outputTokens,
|
|
402
|
+
reasoningOutputTokens,
|
|
403
|
+
totalTokens,
|
|
404
|
+
};
|
|
405
|
+
}
|
|
365
406
|
function rewriteLocalTargetForDockerRuntime(target) {
|
|
366
407
|
const trimmed = target.trim();
|
|
367
408
|
if (!trimmed) {
|
|
@@ -451,89 +492,34 @@ function extractThreadNameUpdateFromNotification(notification) {
|
|
|
451
492
|
normalizeNonEmptyString(params.thread_name);
|
|
452
493
|
return { sdkThreadId, threadName };
|
|
453
494
|
}
|
|
454
|
-
function
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
function isUnimplementedGrpcMethod(error) {
|
|
458
|
-
return isGrpcServiceError(error) && error.code === grpc.status.UNIMPLEMENTED;
|
|
459
|
-
}
|
|
460
|
-
function normalizeAccessTokenExpiration(accessTokenExpiresUnixTimeMs) {
|
|
461
|
-
const rawUnixTimeMs = Number(accessTokenExpiresUnixTimeMs);
|
|
462
|
-
const expirationUnixTimeMs = Number.isFinite(rawUnixTimeMs) && rawUnixTimeMs > 0
|
|
463
|
-
? Math.floor(rawUnixTimeMs)
|
|
464
|
-
: Date.now() + 60 * 60000;
|
|
465
|
-
return {
|
|
466
|
-
accessTokenExpiresUnixTimeMs: expirationUnixTimeMs.toString(),
|
|
467
|
-
accessTokenExpiration: new Date(expirationUnixTimeMs).toISOString(),
|
|
468
|
-
};
|
|
469
|
-
}
|
|
470
|
-
async function loadRuntimeGithubInstallations(apiClient, options, logger) {
|
|
471
|
-
let installationIds = [];
|
|
472
|
-
try {
|
|
473
|
-
const listResponse = await apiClient.listGithubInstallationsForRunner(options);
|
|
474
|
-
installationIds = listResponse.installations.map((installation) => installation.installationId);
|
|
495
|
+
function extractThreadTokenUsageUpdateFromNotification(notification) {
|
|
496
|
+
if (notification.method !== "thread/tokenUsage/updated") {
|
|
497
|
+
return null;
|
|
475
498
|
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
499
|
+
const rawParams = notification.params;
|
|
500
|
+
const sdkThreadId = normalizeNonEmptyString(rawParams.threadId) ??
|
|
501
|
+
normalizeNonEmptyString(rawParams.thread_id);
|
|
502
|
+
const sdkTurnId = normalizeNonEmptyString(rawParams.turnId) ??
|
|
503
|
+
normalizeNonEmptyString(rawParams.turn_id);
|
|
504
|
+
const tokenUsage = isRecord(rawParams.tokenUsage) ? rawParams.tokenUsage : rawParams.token_usage;
|
|
505
|
+
if (!sdkThreadId || !sdkTurnId || !isRecord(tokenUsage)) {
|
|
506
|
+
return null;
|
|
482
507
|
}
|
|
483
|
-
const
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
const accessToken = accessTokenResponse.accessToken.trim();
|
|
488
|
-
if (!accessToken) {
|
|
489
|
-
logger.warn(`Received empty GitHub access token for installation ${installationId.toString()}; skipping.`);
|
|
490
|
-
continue;
|
|
491
|
-
}
|
|
492
|
-
const expiration = normalizeAccessTokenExpiration(accessTokenResponse.accessTokenExpiresUnixTimeMs);
|
|
493
|
-
const repositories = [...new Set(accessTokenResponse.repositories.filter((repository) => repository.trim().length > 0))]
|
|
494
|
-
.sort((left, right) => left.localeCompare(right));
|
|
495
|
-
installationDetails.push({
|
|
496
|
-
installationId: accessTokenResponse.installationId.toString(),
|
|
497
|
-
accessToken,
|
|
498
|
-
accessTokenExpiresUnixTimeMs: expiration.accessTokenExpiresUnixTimeMs,
|
|
499
|
-
accessTokenExpiration: expiration.accessTokenExpiration,
|
|
500
|
-
repositories,
|
|
501
|
-
});
|
|
502
|
-
}
|
|
503
|
-
catch (error) {
|
|
504
|
-
const warning = isUnimplementedGrpcMethod(error)
|
|
505
|
-
? "CompanyHelm API does not implement getGithubInstallationAccessTokenForRunner yet."
|
|
506
|
-
: `Failed to fetch GitHub access token for installation ${installationId.toString()}: ${toErrorMessage(error)}`;
|
|
507
|
-
logger.warn(warning);
|
|
508
|
-
}
|
|
508
|
+
const totalUsage = resolveTokenUsageBreakdown(tokenUsage.total);
|
|
509
|
+
const lastUsage = resolveTokenUsageBreakdown(tokenUsage.last);
|
|
510
|
+
if (!totalUsage || !lastUsage) {
|
|
511
|
+
return null;
|
|
509
512
|
}
|
|
510
|
-
return installationDetails;
|
|
511
|
-
}
|
|
512
|
-
function buildWorkspaceGithubInstallationsPayload(installations) {
|
|
513
513
|
return {
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
access_token_expiration: installation.accessTokenExpiration,
|
|
520
|
-
repositories: installation.repositories,
|
|
521
|
-
})),
|
|
514
|
+
sdkThreadId,
|
|
515
|
+
sdkTurnId,
|
|
516
|
+
totalUsage,
|
|
517
|
+
lastUsage,
|
|
518
|
+
modelContextWindow: normalizeNonNegativeNumber(tokenUsage.modelContextWindow ?? tokenUsage.model_context_window) ?? null,
|
|
522
519
|
};
|
|
523
520
|
}
|
|
524
|
-
function
|
|
525
|
-
|
|
526
|
-
const installationsPath = (0, node_path_1.join)(installationsDirectory, WORKSPACE_INSTALLATIONS_FILENAME);
|
|
527
|
-
const temporaryPath = `${installationsPath}.tmp`;
|
|
528
|
-
const serializedPayload = `${JSON.stringify(payload, null, 2)}\n`;
|
|
529
|
-
try {
|
|
530
|
-
(0, node_fs_1.mkdirSync)(installationsDirectory, { recursive: true });
|
|
531
|
-
(0, node_fs_1.writeFileSync)(temporaryPath, serializedPayload, "utf8");
|
|
532
|
-
(0, node_fs_1.renameSync)(temporaryPath, installationsPath);
|
|
533
|
-
}
|
|
534
|
-
catch (error) {
|
|
535
|
-
logger.warn(`Failed writing GitHub installations file for workspace '${workspaceDirectory}': ${toErrorMessage(error)}`);
|
|
536
|
-
}
|
|
521
|
+
function isGrpcServiceError(error) {
|
|
522
|
+
return Boolean(error && typeof error === "object" && "code" in error);
|
|
537
523
|
}
|
|
538
524
|
function isHttpsRepositoryUrl(value) {
|
|
539
525
|
try {
|
|
@@ -852,65 +838,6 @@ function readWorkspaceThreadMcpConfig(workspaceDirectory, logger) {
|
|
|
852
838
|
return [];
|
|
853
839
|
}
|
|
854
840
|
}
|
|
855
|
-
function resolveThreadAgentCliConfigPath(workspaceDirectory) {
|
|
856
|
-
return (0, node_path_1.join)(workspaceDirectory, WORKSPACE_INSTALLATIONS_DIRECTORY, THREAD_AGENT_CLI_CONFIG_FILENAME);
|
|
857
|
-
}
|
|
858
|
-
function parseThreadAgentCliConfig(content) {
|
|
859
|
-
if (!isRecord(content)) {
|
|
860
|
-
return null;
|
|
861
|
-
}
|
|
862
|
-
const agentApiUrl = normalizeNonEmptyString(content.agent_api_url);
|
|
863
|
-
const token = normalizeNonEmptyString(content.token);
|
|
864
|
-
if (!agentApiUrl || !token) {
|
|
865
|
-
return null;
|
|
866
|
-
}
|
|
867
|
-
return {
|
|
868
|
-
agent_api_url: agentApiUrl,
|
|
869
|
-
token,
|
|
870
|
-
};
|
|
871
|
-
}
|
|
872
|
-
function writeWorkspaceThreadAgentCliConfig(workspaceDirectory, cliSecret, agentApiUrl, logger) {
|
|
873
|
-
const configPath = resolveThreadAgentCliConfigPath(workspaceDirectory);
|
|
874
|
-
const configDirectory = (0, node_path_1.join)(workspaceDirectory, WORKSPACE_INSTALLATIONS_DIRECTORY);
|
|
875
|
-
const temporaryPath = `${configPath}.tmp`;
|
|
876
|
-
try {
|
|
877
|
-
(0, node_fs_1.mkdirSync)(configDirectory, { recursive: true });
|
|
878
|
-
if (cliSecret.length === 0) {
|
|
879
|
-
(0, node_fs_1.rmSync)(configPath, { force: true });
|
|
880
|
-
(0, node_fs_1.rmSync)(temporaryPath, { force: true });
|
|
881
|
-
return;
|
|
882
|
-
}
|
|
883
|
-
const payload = {
|
|
884
|
-
agent_api_url: normalizeThreadAgentApiUrlForRuntime(agentApiUrl),
|
|
885
|
-
token: cliSecret,
|
|
886
|
-
};
|
|
887
|
-
(0, node_fs_1.writeFileSync)(temporaryPath, `${JSON.stringify(payload, null, 2)}\n`, "utf8");
|
|
888
|
-
(0, node_fs_1.renameSync)(temporaryPath, configPath);
|
|
889
|
-
}
|
|
890
|
-
catch (error) {
|
|
891
|
-
logger.warn(`Failed writing thread agent CLI config for workspace '${workspaceDirectory}': ${toErrorMessage(error)}`);
|
|
892
|
-
}
|
|
893
|
-
}
|
|
894
|
-
function readWorkspaceThreadAgentCliConfig(workspaceDirectory, logger) {
|
|
895
|
-
const configPath = resolveThreadAgentCliConfigPath(workspaceDirectory);
|
|
896
|
-
try {
|
|
897
|
-
const rawContent = (0, node_fs_1.readFileSync)(configPath, "utf8");
|
|
898
|
-
const parsedContent = JSON.parse(rawContent);
|
|
899
|
-
const parsedConfig = parseThreadAgentCliConfig(parsedContent);
|
|
900
|
-
if (!parsedConfig) {
|
|
901
|
-
logger.warn(`Thread agent CLI config has invalid shape at '${configPath}'.`);
|
|
902
|
-
return null;
|
|
903
|
-
}
|
|
904
|
-
return parsedConfig;
|
|
905
|
-
}
|
|
906
|
-
catch (error) {
|
|
907
|
-
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
908
|
-
return null;
|
|
909
|
-
}
|
|
910
|
-
logger.warn(`Failed reading thread agent CLI config at '${configPath}': ${toErrorMessage(error)}`);
|
|
911
|
-
return null;
|
|
912
|
-
}
|
|
913
|
-
}
|
|
914
841
|
function escapeTomlString(value) {
|
|
915
842
|
return JSON.stringify(value);
|
|
916
843
|
}
|
|
@@ -1112,7 +1039,6 @@ async function reconcileThreadRunningStateBeforeUserMessage(cfg, threadState, lo
|
|
|
1112
1039
|
const persistedThreadMcpServers = metadataStore.readThreadMcpConfig(threadState.id);
|
|
1113
1040
|
const persistedThreadGitSkillPackages = metadataStore.readThreadGitSkillsConfig(threadState.id);
|
|
1114
1041
|
const threadMcpSetup = buildThreadCodexMcpSetup(persistedThreadMcpServers);
|
|
1115
|
-
const threadAgentCliConfig = buildThreadAgentCliConfig(threadState.cliSecret, cfg.agent_api_url);
|
|
1116
1042
|
const appServerSession = await getOrCreateThreadAppServerSession(threadState.id, threadState.runtimeContainer, threadMcpSetup.appServerEnv, cfg.codex.app_server_client_name, logger);
|
|
1117
1043
|
const runtimeUser = buildThreadRuntimeUser(cfg, threadState);
|
|
1118
1044
|
await (0, thread_runtime_js_1.ensureThreadRuntimeReady)({
|
|
@@ -1127,11 +1053,7 @@ async function reconcileThreadRunningStateBeforeUserMessage(cfg, threadState, lo
|
|
|
1127
1053
|
await containerService.ensureRuntimeContainerThreadMetadata(threadState.runtimeContainer, runtimeUser, {
|
|
1128
1054
|
mcpServers: persistedThreadMcpServers,
|
|
1129
1055
|
gitSkillPackages: persistedThreadGitSkillPackages,
|
|
1130
|
-
threadAgentCliConfig,
|
|
1131
1056
|
});
|
|
1132
|
-
if (threadAgentCliConfig) {
|
|
1133
|
-
await containerService.ensureRuntimeContainerAgentCliConfig(threadState.runtimeContainer, runtimeUser, threadAgentCliConfig);
|
|
1134
|
-
}
|
|
1135
1057
|
if (!appServerSession.started) {
|
|
1136
1058
|
await containerService.ensureRuntimeContainerCodexConfig(threadState.runtimeContainer, runtimeUser, threadMcpSetup.configToml);
|
|
1137
1059
|
}
|
|
@@ -1163,73 +1085,6 @@ async function reconcileThreadRunningStateBeforeUserMessage(cfg, threadState, lo
|
|
|
1163
1085
|
isCurrentTurnRunning: false,
|
|
1164
1086
|
};
|
|
1165
1087
|
}
|
|
1166
|
-
async function listTrackedThreadRuntimeTargets(cfg, logger) {
|
|
1167
|
-
const { db, client } = await (0, db_js_1.initDb)(cfg.state_db_path);
|
|
1168
|
-
try {
|
|
1169
|
-
return await db
|
|
1170
|
-
.select({
|
|
1171
|
-
threadId: schema_js_1.threads.id,
|
|
1172
|
-
runtimeContainer: schema_js_1.threads.runtimeContainer,
|
|
1173
|
-
homeDirectory: schema_js_1.threads.homeDirectory,
|
|
1174
|
-
uid: schema_js_1.threads.uid,
|
|
1175
|
-
gid: schema_js_1.threads.gid,
|
|
1176
|
-
})
|
|
1177
|
-
.from(schema_js_1.threads)
|
|
1178
|
-
.all();
|
|
1179
|
-
}
|
|
1180
|
-
catch (error) {
|
|
1181
|
-
logger.warn(`Failed to list tracked thread runtimes for GitHub installation sync: ${toErrorMessage(error)}`);
|
|
1182
|
-
return [];
|
|
1183
|
-
}
|
|
1184
|
-
finally {
|
|
1185
|
-
client.close();
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
|
-
function resolveGithubInstallationsSyncDelayMs(installations) {
|
|
1189
|
-
let syncDelayMs = GITHUB_INSTALLATIONS_SYNC_INTERVAL_MS;
|
|
1190
|
-
const now = Date.now();
|
|
1191
|
-
for (const installation of installations) {
|
|
1192
|
-
const expirationUnixTimeMs = Number(installation.accessTokenExpiresUnixTimeMs);
|
|
1193
|
-
if (!Number.isFinite(expirationUnixTimeMs) || expirationUnixTimeMs <= 0) {
|
|
1194
|
-
continue;
|
|
1195
|
-
}
|
|
1196
|
-
const refreshInMs = expirationUnixTimeMs - now - GITHUB_INSTALLATIONS_REFRESH_WINDOW_MS;
|
|
1197
|
-
const boundedRefreshDelayMs = Math.max(GITHUB_INSTALLATIONS_MIN_SYNC_INTERVAL_MS, Math.min(GITHUB_INSTALLATIONS_SYNC_INTERVAL_MS, refreshInMs));
|
|
1198
|
-
syncDelayMs = Math.min(syncDelayMs, boundedRefreshDelayMs);
|
|
1199
|
-
}
|
|
1200
|
-
return Math.max(GITHUB_INSTALLATIONS_MIN_SYNC_INTERVAL_MS, Math.min(GITHUB_INSTALLATIONS_SYNC_INTERVAL_MS, syncDelayMs));
|
|
1201
|
-
}
|
|
1202
|
-
async function syncGithubInstallationsForRuntimeTargets(cfg, apiClient, options, runtimeTargets, logger) {
|
|
1203
|
-
const uniqueTargets = [
|
|
1204
|
-
...new Map(runtimeTargets
|
|
1205
|
-
.filter((target) => target.runtimeContainer.trim().length > 0)
|
|
1206
|
-
.map((target) => [target.runtimeContainer, target])).values(),
|
|
1207
|
-
];
|
|
1208
|
-
if (uniqueTargets.length === 0) {
|
|
1209
|
-
return [];
|
|
1210
|
-
}
|
|
1211
|
-
const installations = await loadRuntimeGithubInstallations(apiClient, options, logger);
|
|
1212
|
-
const payload = buildWorkspaceGithubInstallationsPayload(installations);
|
|
1213
|
-
const containerService = new thread_lifecycle_js_1.ThreadContainerService();
|
|
1214
|
-
for (const target of uniqueTargets) {
|
|
1215
|
-
try {
|
|
1216
|
-
if (!await containerService.isContainerRunning(target.runtimeContainer)) {
|
|
1217
|
-
continue;
|
|
1218
|
-
}
|
|
1219
|
-
await containerService.ensureRuntimeContainerGithubInstallations(target.runtimeContainer, {
|
|
1220
|
-
uid: target.uid,
|
|
1221
|
-
gid: target.gid,
|
|
1222
|
-
agentUser: cfg.agent_user,
|
|
1223
|
-
agentHomeDirectory: target.homeDirectory,
|
|
1224
|
-
}, payload);
|
|
1225
|
-
}
|
|
1226
|
-
catch (error) {
|
|
1227
|
-
logger.warn(`Failed syncing GitHub installations into runtime container '${target.runtimeContainer}': ${toErrorMessage(error)}`);
|
|
1228
|
-
}
|
|
1229
|
-
}
|
|
1230
|
-
logger.debug(`Synced ${installations.length} GitHub installation token(s) to ${uniqueTargets.length} runtime container(s).`);
|
|
1231
|
-
return installations;
|
|
1232
|
-
}
|
|
1233
1088
|
async function waitForAbort(signal, delayMs) {
|
|
1234
1089
|
if (signal.aborted) {
|
|
1235
1090
|
return;
|
|
@@ -1247,21 +1102,6 @@ async function waitForAbort(signal, delayMs) {
|
|
|
1247
1102
|
signal.addEventListener("abort", handleAbort);
|
|
1248
1103
|
});
|
|
1249
1104
|
}
|
|
1250
|
-
async function runGithubInstallationsSyncLoop(cfg, apiClient, options, logger, signal) {
|
|
1251
|
-
while (!signal.aborted) {
|
|
1252
|
-
let nextDelayMs = GITHUB_INSTALLATIONS_SYNC_INTERVAL_MS;
|
|
1253
|
-
try {
|
|
1254
|
-
const runtimeTargets = await listTrackedThreadRuntimeTargets(cfg, logger);
|
|
1255
|
-
const installations = await syncGithubInstallationsForRuntimeTargets(cfg, apiClient, options, runtimeTargets, logger);
|
|
1256
|
-
nextDelayMs = resolveGithubInstallationsSyncDelayMs(installations);
|
|
1257
|
-
}
|
|
1258
|
-
catch (error) {
|
|
1259
|
-
logger.warn(`GitHub installation sync loop iteration failed: ${toErrorMessage(error)}`);
|
|
1260
|
-
nextDelayMs = GITHUB_INSTALLATIONS_MIN_SYNC_INTERVAL_MS;
|
|
1261
|
-
}
|
|
1262
|
-
await waitForAbort(signal, nextDelayMs);
|
|
1263
|
-
}
|
|
1264
|
-
}
|
|
1265
1105
|
function normalizeReasoningEffort(value) {
|
|
1266
1106
|
if (!value) {
|
|
1267
1107
|
return null;
|
|
@@ -1279,21 +1119,12 @@ function normalizeAdditionalModelInstructions(value) {
|
|
|
1279
1119
|
const trimmed = value.trim();
|
|
1280
1120
|
return trimmed.length > 0 ? trimmed : null;
|
|
1281
1121
|
}
|
|
1282
|
-
function
|
|
1283
|
-
const normalizedSecret = normalizeNonEmptyString(cliSecret);
|
|
1284
|
-
if (!normalizedSecret) {
|
|
1285
|
-
return null;
|
|
1286
|
-
}
|
|
1287
|
-
return {
|
|
1288
|
-
agent_api_url: normalizeThreadAgentApiUrlForRuntime(agentApiUrl),
|
|
1289
|
-
token: normalizedSecret,
|
|
1290
|
-
};
|
|
1291
|
-
}
|
|
1292
|
-
function buildThreadDeveloperInstructions(cfg, additionalModelInstructions, cliSecret) {
|
|
1122
|
+
function buildThreadDeveloperInstructions(threadId, cfg, additionalModelInstructions, cliSecret) {
|
|
1293
1123
|
return (0, system_prompt_js_1.buildCodexDeveloperInstructions)(additionalModelInstructions, {
|
|
1294
1124
|
homeDirectory: cfg.agent_home_directory,
|
|
1295
1125
|
agentApiUrl: normalizeThreadAgentApiUrlForRuntime(cfg.agent_api_url),
|
|
1296
1126
|
agentToken: normalizeNonEmptyString(cliSecret) ?? "<thread-secret>",
|
|
1127
|
+
threadId,
|
|
1297
1128
|
workspaceMode: cfg.use_dedicated_workspaces ? "dedicated" : "shared",
|
|
1298
1129
|
});
|
|
1299
1130
|
}
|
|
@@ -1544,6 +1375,32 @@ async function sendThreadNameUpdate(commandChannel, threadId, threadName) {
|
|
|
1544
1375
|
});
|
|
1545
1376
|
await commandChannel.send(message);
|
|
1546
1377
|
}
|
|
1378
|
+
function toProtoTokenUsageBreakdown(usage) {
|
|
1379
|
+
return {
|
|
1380
|
+
inputTokens: BigInt(usage.inputTokens),
|
|
1381
|
+
cachedInputTokens: BigInt(usage.cachedInputTokens),
|
|
1382
|
+
outputTokens: BigInt(usage.outputTokens),
|
|
1383
|
+
reasoningOutputTokens: BigInt(usage.reasoningOutputTokens),
|
|
1384
|
+
totalTokens: BigInt(usage.totalTokens),
|
|
1385
|
+
};
|
|
1386
|
+
}
|
|
1387
|
+
async function sendThreadTokenUsageUpdate(commandChannel, threadId, tokenUsageUpdate) {
|
|
1388
|
+
const message = (0, protobuf_1.create)(protos_1.ClientMessageSchema, {
|
|
1389
|
+
payload: {
|
|
1390
|
+
case: "threadTokenUsageUpdate",
|
|
1391
|
+
value: {
|
|
1392
|
+
threadId,
|
|
1393
|
+
sdkTurnId: tokenUsageUpdate.sdkTurnId,
|
|
1394
|
+
totalUsage: toProtoTokenUsageBreakdown(tokenUsageUpdate.totalUsage),
|
|
1395
|
+
lastUsage: toProtoTokenUsageBreakdown(tokenUsageUpdate.lastUsage),
|
|
1396
|
+
modelContextWindow: tokenUsageUpdate.modelContextWindow === null
|
|
1397
|
+
? undefined
|
|
1398
|
+
: BigInt(tokenUsageUpdate.modelContextWindow),
|
|
1399
|
+
},
|
|
1400
|
+
},
|
|
1401
|
+
});
|
|
1402
|
+
await commandChannel.send(message);
|
|
1403
|
+
}
|
|
1547
1404
|
async function sendTurnExecutionUpdate(commandChannel, threadId, sdkTurnId, status, requestId) {
|
|
1548
1405
|
const message = (0, protobuf_1.create)(protos_1.ClientMessageSchema, {
|
|
1549
1406
|
requestId,
|
|
@@ -1855,7 +1712,6 @@ async function handleCreateThreadRequest(cfg, commandChannel, request, requestId
|
|
|
1855
1712
|
const persistedThreadMcpServers = metadataStore.readThreadMcpConfig(threadState.id);
|
|
1856
1713
|
const persistedThreadGitSkillPackages = metadataStore.readThreadGitSkillsConfig(threadState.id);
|
|
1857
1714
|
const threadMcpSetup = buildThreadCodexMcpSetup(persistedThreadMcpServers);
|
|
1858
|
-
const threadAgentCliConfig = buildThreadAgentCliConfig(threadState.cliSecret, cfg.agent_api_url);
|
|
1859
1715
|
const appServerSession = await getOrCreateThreadAppServerSession(threadId, threadState.runtimeContainer, threadMcpSetup.appServerEnv, cfg.codex.app_server_client_name, logger);
|
|
1860
1716
|
const runtimeUser = {
|
|
1861
1717
|
uid: threadState.uid,
|
|
@@ -1875,25 +1731,12 @@ async function handleCreateThreadRequest(cfg, commandChannel, request, requestId
|
|
|
1875
1731
|
await containerService.ensureRuntimeContainerThreadMetadata(threadState.runtimeContainer, runtimeUser, {
|
|
1876
1732
|
mcpServers: persistedThreadMcpServers,
|
|
1877
1733
|
gitSkillPackages: persistedThreadGitSkillPackages,
|
|
1878
|
-
threadAgentCliConfig,
|
|
1879
1734
|
});
|
|
1880
|
-
if (threadAgentCliConfig) {
|
|
1881
|
-
await containerService.ensureRuntimeContainerAgentCliConfig(threadState.runtimeContainer, runtimeUser, threadAgentCliConfig);
|
|
1882
|
-
}
|
|
1883
|
-
await syncGithubInstallationsForRuntimeTargets(cfg, apiClient, apiCallOptions, [
|
|
1884
|
-
{
|
|
1885
|
-
threadId: threadState.id,
|
|
1886
|
-
runtimeContainer: threadState.runtimeContainer,
|
|
1887
|
-
homeDirectory: threadState.homeDirectory,
|
|
1888
|
-
uid: threadState.uid,
|
|
1889
|
-
gid: threadState.gid,
|
|
1890
|
-
},
|
|
1891
|
-
], logger);
|
|
1892
1735
|
if (!appServerSession.started) {
|
|
1893
1736
|
await containerService.ensureRuntimeContainerCodexConfig(threadState.runtimeContainer, runtimeUser, threadMcpSetup.configToml);
|
|
1894
1737
|
}
|
|
1895
1738
|
await ensureThreadAppServerSessionStarted(appServerSession);
|
|
1896
|
-
const developerInstructions = buildThreadDeveloperInstructions(cfg, threadState.additionalModelInstructions, threadState.cliSecret);
|
|
1739
|
+
const developerInstructions = buildThreadDeveloperInstructions(threadId, cfg, threadState.additionalModelInstructions, threadState.cliSecret);
|
|
1897
1740
|
logger.debug(`Starting app-server thread '${threadId}' with developer instructions: ${JSON.stringify(developerInstructions)}.`);
|
|
1898
1741
|
const threadStartResponse = await appServerSession.appServer.startThreadWithResponse({
|
|
1899
1742
|
model: threadState.model,
|
|
@@ -2105,6 +1948,12 @@ async function waitForThreadTurnCompletion(stateDbPath, appServer, commandChanne
|
|
|
2105
1948
|
receivedThreadNameUpdate = true;
|
|
2106
1949
|
await sendThreadNameUpdate(commandChannel, threadId, threadNameUpdate.threadName);
|
|
2107
1950
|
}
|
|
1951
|
+
const tokenUsageUpdate = extractThreadTokenUsageUpdateFromNotification(notification);
|
|
1952
|
+
if (tokenUsageUpdate &&
|
|
1953
|
+
tokenUsageUpdate.sdkThreadId === sdkThreadId &&
|
|
1954
|
+
tokenUsageUpdate.sdkTurnId === sdkTurnId) {
|
|
1955
|
+
await sendThreadTokenUsageUpdate(commandChannel, threadId, tokenUsageUpdate);
|
|
1956
|
+
}
|
|
2108
1957
|
if (notification.method === "item/started" &&
|
|
2109
1958
|
notification.params.threadId === sdkThreadId &&
|
|
2110
1959
|
notification.params.turnId === sdkTurnId) {
|
|
@@ -2149,7 +1998,6 @@ async function executeCreateUserMessageRequest(cfg, commandChannel, request, req
|
|
|
2149
1998
|
const persistedThreadMcpServers = metadataStore.readThreadMcpConfig(threadState.id);
|
|
2150
1999
|
const persistedThreadGitSkillPackages = metadataStore.readThreadGitSkillsConfig(threadState.id);
|
|
2151
2000
|
const threadMcpSetup = buildThreadCodexMcpSetup(persistedThreadMcpServers);
|
|
2152
|
-
const threadAgentCliConfig = buildThreadAgentCliConfig(threadState.cliSecret, cfg.agent_api_url);
|
|
2153
2001
|
const appServerSession = await getOrCreateThreadAppServerSession(request.threadId, threadState.runtimeContainer, threadMcpSetup.appServerEnv, cfg.codex.app_server_client_name, logger);
|
|
2154
2002
|
const appServer = appServerSession.appServer;
|
|
2155
2003
|
const runtimeUser = buildThreadRuntimeUser(cfg, threadState);
|
|
@@ -2173,11 +2021,7 @@ async function executeCreateUserMessageRequest(cfg, commandChannel, request, req
|
|
|
2173
2021
|
await containerService.ensureRuntimeContainerThreadMetadata(threadState.runtimeContainer, runtimeUser, {
|
|
2174
2022
|
mcpServers: persistedThreadMcpServers,
|
|
2175
2023
|
gitSkillPackages: persistedThreadGitSkillPackages,
|
|
2176
|
-
threadAgentCliConfig,
|
|
2177
2024
|
});
|
|
2178
|
-
if (threadAgentCliConfig) {
|
|
2179
|
-
await containerService.ensureRuntimeContainerAgentCliConfig(threadState.runtimeContainer, runtimeUser, threadAgentCliConfig);
|
|
2180
|
-
}
|
|
2181
2025
|
if (!appServerSession.started) {
|
|
2182
2026
|
await containerService.ensureRuntimeContainerCodexConfig(threadState.runtimeContainer, runtimeUser, threadMcpSetup.configToml);
|
|
2183
2027
|
}
|
|
@@ -2201,7 +2045,7 @@ async function executeCreateUserMessageRequest(cfg, commandChannel, request, req
|
|
|
2201
2045
|
await updateThreadTurnState(cfg, request.threadId, { sdkThreadId });
|
|
2202
2046
|
}
|
|
2203
2047
|
else {
|
|
2204
|
-
const developerInstructions = buildThreadDeveloperInstructions(cfg, threadState.additionalModelInstructions, threadState.cliSecret);
|
|
2048
|
+
const developerInstructions = buildThreadDeveloperInstructions(request.threadId, cfg, threadState.additionalModelInstructions, threadState.cliSecret);
|
|
2205
2049
|
const threadStartParams = {
|
|
2206
2050
|
model: request.model ?? threadState.model,
|
|
2207
2051
|
modelProvider: null,
|
|
@@ -2644,6 +2488,7 @@ function sendDaemonParentMessage(message) {
|
|
|
2644
2488
|
async function runRootCommand(options, runtimeOptions) {
|
|
2645
2489
|
const logger = (0, logger_js_1.createLogger)(options.logLevel ?? "INFO", { daemonMode: options.daemon ?? false });
|
|
2646
2490
|
const cfg = buildRootConfig(options);
|
|
2491
|
+
logger.info(formatWorkspaceStartupMessage(cfg));
|
|
2647
2492
|
await (0, entrypoints_js_1.ensureRunnerStartupPreflight)(cfg);
|
|
2648
2493
|
await (0, auth_js_1.ensureCodexRunnerStartState)(cfg, {
|
|
2649
2494
|
useDedicatedAuth: options.useDedicatedAuth,
|
|
@@ -2674,8 +2519,6 @@ async function runRootCommand(options, runtimeOptions) {
|
|
|
2674
2519
|
const apiClient = new companyhelm_api_client_js_1.CompanyhelmApiClient({ apiUrl: cfg.companyhelm_api_url, logger });
|
|
2675
2520
|
activeApiClient = apiClient;
|
|
2676
2521
|
let commandChannel = null;
|
|
2677
|
-
let githubInstallationsSyncAbortController = null;
|
|
2678
|
-
let githubInstallationsSyncTask = null;
|
|
2679
2522
|
try {
|
|
2680
2523
|
reconnectAttempt += 1;
|
|
2681
2524
|
commandChannel = await apiClient.connect(registerRequest, apiCallOptions);
|
|
@@ -2690,12 +2533,6 @@ async function runRootCommand(options, runtimeOptions) {
|
|
|
2690
2533
|
logger.info(`Connected to CompanyHelm API at ${cfg.companyhelm_api_url}`);
|
|
2691
2534
|
}
|
|
2692
2535
|
reconnectAttempt = 0;
|
|
2693
|
-
githubInstallationsSyncAbortController = new AbortController();
|
|
2694
|
-
githubInstallationsSyncTask = runGithubInstallationsSyncLoop(cfg, apiClient, apiCallOptions, logger, githubInstallationsSyncAbortController.signal).catch((error) => {
|
|
2695
|
-
if (!githubInstallationsSyncAbortController?.signal.aborted) {
|
|
2696
|
-
logger.warn(`GitHub installation sync loop exited unexpectedly: ${toErrorMessage(error)}`);
|
|
2697
|
-
}
|
|
2698
|
-
});
|
|
2699
2536
|
await raceWithAbort(runCommandLoop(cfg, commandChannel, commandMessageSink, apiClient, apiCallOptions, logger), interruptState.signal);
|
|
2700
2537
|
logger.warn("CompanyHelm API command channel closed. Reconnecting...");
|
|
2701
2538
|
}
|
|
@@ -2715,10 +2552,6 @@ async function runRootCommand(options, runtimeOptions) {
|
|
|
2715
2552
|
"Retrying...");
|
|
2716
2553
|
}
|
|
2717
2554
|
finally {
|
|
2718
|
-
if (githubInstallationsSyncAbortController) {
|
|
2719
|
-
githubInstallationsSyncAbortController.abort();
|
|
2720
|
-
}
|
|
2721
|
-
void githubInstallationsSyncTask;
|
|
2722
2555
|
if (commandChannel) {
|
|
2723
2556
|
commandChannel.cancel();
|
|
2724
2557
|
commandMessageSink.unbind(commandChannel);
|
|
@@ -42,16 +42,6 @@ class RuntimeProvisioningScriptRenderer {
|
|
|
42
42
|
config_content: shellQuote(configToml),
|
|
43
43
|
});
|
|
44
44
|
}
|
|
45
|
-
renderAgentCliConfigScript(user, config) {
|
|
46
|
-
const configDirectory = (0, node_path_1.join)(user.agentHomeDirectory, ".config", "companyhelm-agent-cli");
|
|
47
|
-
const configPath = (0, node_path_1.join)(configDirectory, "config.json");
|
|
48
|
-
const configContent = `${JSON.stringify(config, null, 2)}\n`;
|
|
49
|
-
return this.templateRenderer.render("provisioning/runtime_agent_cli_config.sh.j2", {
|
|
50
|
-
config_dir: shellQuote(configDirectory),
|
|
51
|
-
config_path: shellQuote(configPath),
|
|
52
|
-
config_content: shellQuote(configContent),
|
|
53
|
-
});
|
|
54
|
-
}
|
|
55
45
|
renderGitConfigScript(gitUserName, gitUserEmail) {
|
|
56
46
|
return this.templateRenderer.render("provisioning/runtime_git_config.sh.j2", {
|
|
57
47
|
default_git_user_name: shellQuote(gitUserName),
|
|
@@ -20,6 +20,7 @@ class RuntimeSystemPromptRenderer {
|
|
|
20
20
|
home_directory: options.homeDirectory,
|
|
21
21
|
agent_api_url: options.agentApiUrl,
|
|
22
22
|
agent_token: options.agentToken,
|
|
23
|
+
thread_id: options.threadId,
|
|
23
24
|
};
|
|
24
25
|
const common = this.templateRenderer.render("system_prompts/common.md.j2", context).trim();
|
|
25
26
|
const workspaceSpecificTemplate = options.workspaceMode === "dedicated"
|
|
@@ -120,24 +120,6 @@ function createAgentRunnerControlServiceDefinition(pathPrefix = "") {
|
|
|
120
120
|
responseSerialize: (response) => Buffer.from((0, protobuf_1.toBinary)(protos_1.ServerMessageSchema, response)),
|
|
121
121
|
responseDeserialize: (bytes) => (0, protobuf_1.fromBinary)(protos_1.ServerMessageSchema, bytes),
|
|
122
122
|
},
|
|
123
|
-
listGithubInstallationsForRunner: {
|
|
124
|
-
path: buildRpcPath(methods.listGithubInstallations.name, pathPrefix),
|
|
125
|
-
requestStream: false,
|
|
126
|
-
responseStream: false,
|
|
127
|
-
requestSerialize: (request) => Buffer.from((0, protobuf_1.toBinary)(protos_1.ListGithubInstallationsRequestSchema, request)),
|
|
128
|
-
requestDeserialize: (bytes) => (0, protobuf_1.fromBinary)(protos_1.ListGithubInstallationsRequestSchema, bytes),
|
|
129
|
-
responseSerialize: (response) => Buffer.from((0, protobuf_1.toBinary)(protos_1.ListGithubInstallationsResponseSchema, response)),
|
|
130
|
-
responseDeserialize: (bytes) => (0, protobuf_1.fromBinary)(protos_1.ListGithubInstallationsResponseSchema, bytes),
|
|
131
|
-
},
|
|
132
|
-
getGithubInstallationAccessTokenForRunner: {
|
|
133
|
-
path: buildRpcPath(methods.githubInstallationAccessToken.name, pathPrefix),
|
|
134
|
-
requestStream: false,
|
|
135
|
-
responseStream: false,
|
|
136
|
-
requestSerialize: (request) => Buffer.from((0, protobuf_1.toBinary)(protos_1.GithubInstallationAccessTokenRequestSchema, request)),
|
|
137
|
-
requestDeserialize: (bytes) => (0, protobuf_1.fromBinary)(protos_1.GithubInstallationAccessTokenRequestSchema, bytes),
|
|
138
|
-
responseSerialize: (response) => Buffer.from((0, protobuf_1.toBinary)(protos_1.GithubInstallationAccessTokenResponseSchema, response)),
|
|
139
|
-
responseDeserialize: (bytes) => (0, protobuf_1.fromBinary)(protos_1.GithubInstallationAccessTokenResponseSchema, bytes),
|
|
140
|
-
},
|
|
141
123
|
};
|
|
142
124
|
}
|
|
143
125
|
function createAgentRunnerControlClient(endpoint, credentials, channelOptions) {
|
|
@@ -300,36 +282,6 @@ class CompanyhelmApiClient {
|
|
|
300
282
|
const stream = this.client.controlChannel(options?.metadata, options?.callOptions);
|
|
301
283
|
return new CompanyhelmCommandChannel(stream, this.logger);
|
|
302
284
|
}
|
|
303
|
-
listGithubInstallationsForRunner(options) {
|
|
304
|
-
const metadata = options?.metadata ?? new grpc.Metadata();
|
|
305
|
-
const callOptions = options?.callOptions ?? {};
|
|
306
|
-
const request = (0, protobuf_1.create)(protos_1.ListGithubInstallationsRequestSchema, {});
|
|
307
|
-
return new Promise((resolve, reject) => {
|
|
308
|
-
this.client.listGithubInstallationsForRunner(request, metadata, callOptions, (error, response) => {
|
|
309
|
-
if (error) {
|
|
310
|
-
reject(error);
|
|
311
|
-
return;
|
|
312
|
-
}
|
|
313
|
-
resolve(response ?? (0, protobuf_1.create)(protos_1.ListGithubInstallationsResponseSchema, {}));
|
|
314
|
-
});
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
|
-
getGithubInstallationAccessTokenForRunner(installationId, options) {
|
|
318
|
-
const metadata = options?.metadata ?? new grpc.Metadata();
|
|
319
|
-
const callOptions = options?.callOptions ?? {};
|
|
320
|
-
const request = (0, protobuf_1.create)(protos_1.GithubInstallationAccessTokenRequestSchema, {
|
|
321
|
-
installationId,
|
|
322
|
-
});
|
|
323
|
-
return new Promise((resolve, reject) => {
|
|
324
|
-
this.client.getGithubInstallationAccessTokenForRunner(request, metadata, callOptions, (error, response) => {
|
|
325
|
-
if (error) {
|
|
326
|
-
reject(error);
|
|
327
|
-
return;
|
|
328
|
-
}
|
|
329
|
-
resolve(response ?? (0, protobuf_1.create)(protos_1.GithubInstallationAccessTokenResponseSchema, {}));
|
|
330
|
-
});
|
|
331
|
-
});
|
|
332
|
-
}
|
|
333
285
|
close() {
|
|
334
286
|
this.client.close();
|
|
335
287
|
}
|
|
@@ -313,21 +313,6 @@ function buildRuntimeThreadGitSkillsLinkScript(user, options) {
|
|
|
313
313
|
}
|
|
314
314
|
return scriptLines.join("\n");
|
|
315
315
|
}
|
|
316
|
-
function buildRuntimeAgentCliConfigScript(user, config) {
|
|
317
|
-
const configDirectory = (0, node_path_1.join)(user.agentHomeDirectory, ".config", "companyhelm-agent-cli");
|
|
318
|
-
const configPath = (0, node_path_1.join)(configDirectory, "config.json");
|
|
319
|
-
const configContent = `${JSON.stringify(config, null, 2)}\n`;
|
|
320
|
-
return [
|
|
321
|
-
"set -euo pipefail",
|
|
322
|
-
`CONFIG_DIR=${shellQuote(configDirectory)}`,
|
|
323
|
-
`CONFIG_PATH=${shellQuote(configPath)}`,
|
|
324
|
-
`CONFIG_CONTENT=${shellQuote(configContent)}`,
|
|
325
|
-
"",
|
|
326
|
-
'install -d -m 0755 "$CONFIG_DIR"',
|
|
327
|
-
'printf \'%s\' "$CONFIG_CONTENT" > "$CONFIG_PATH"',
|
|
328
|
-
'chmod 0600 "$CONFIG_PATH"',
|
|
329
|
-
].join("\n");
|
|
330
|
-
}
|
|
331
316
|
function buildDindContainerOptions(options) {
|
|
332
317
|
return {
|
|
333
318
|
name: options.names.dind,
|
|
@@ -627,10 +612,6 @@ class ThreadContainerService {
|
|
|
627
612
|
const script = this.scriptRenderer.renderCodexConfigScript(user, configToml);
|
|
628
613
|
this.runDockerExecScript(["exec", "-u", user.agentUser, name, "bash", "-lc", script], `Failed to write runtime Codex config.toml in container '${name}'`);
|
|
629
614
|
}
|
|
630
|
-
async ensureRuntimeContainerAgentCliConfig(name, user, config) {
|
|
631
|
-
const script = this.scriptRenderer.renderAgentCliConfigScript(user, config);
|
|
632
|
-
this.runDockerExecScript(["exec", "-u", user.agentUser, name, "bash", "-lc", script], `Failed to write runtime agent config in container '${name}'`);
|
|
633
|
-
}
|
|
634
615
|
async ensureRuntimeContainerGitConfig(name, user, gitUserName, gitUserEmail) {
|
|
635
616
|
const script = this.scriptRenderer.renderGitConfigScript(gitUserName, gitUserEmail);
|
|
636
617
|
this.runDockerExecScript(["exec", "-u", user.agentUser, name, "bash", "-lc", script], `Failed to configure git author defaults in runtime container '${name}'`);
|
|
@@ -651,14 +632,6 @@ class ThreadContainerService {
|
|
|
651
632
|
const script = this.scriptRenderer.renderAgentMetadataScript(user, files);
|
|
652
633
|
this.runDockerExecScript(["exec", "-u", user.agentUser, name, "bash", "-lc", script], contextMessage);
|
|
653
634
|
}
|
|
654
|
-
async ensureRuntimeContainerGithubInstallations(name, user, payload) {
|
|
655
|
-
await this.ensureRuntimeContainerAgentMetadataFiles(name, user, [
|
|
656
|
-
{
|
|
657
|
-
filename: "installations.json",
|
|
658
|
-
content: `${JSON.stringify(payload, null, 2)}\n`,
|
|
659
|
-
},
|
|
660
|
-
], `Failed to write runtime GitHub installations metadata in container '${name}'`);
|
|
661
|
-
}
|
|
662
635
|
async ensureRuntimeContainerThreadMetadata(name, user, payload) {
|
|
663
636
|
const files = [
|
|
664
637
|
{
|
|
@@ -670,12 +643,6 @@ class ThreadContainerService {
|
|
|
670
643
|
content: `${JSON.stringify({ packages: payload.gitSkillPackages }, null, 2)}\n`,
|
|
671
644
|
},
|
|
672
645
|
];
|
|
673
|
-
if (payload.threadAgentCliConfig) {
|
|
674
|
-
files.push({
|
|
675
|
-
filename: "thread-agent-cli.json",
|
|
676
|
-
content: `${JSON.stringify(payload.threadAgentCliConfig, null, 2)}\n`,
|
|
677
|
-
});
|
|
678
|
-
}
|
|
679
646
|
await this.ensureRuntimeContainerAgentMetadataFiles(name, user, files, `Failed to write runtime thread metadata in container '${name}'`);
|
|
680
647
|
}
|
|
681
648
|
async stopContainer(name) {
|
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Agent Instructions
|
|
2
2
|
|
|
3
|
+
## Identity
|
|
4
|
+
|
|
5
|
+
- Your CompanyHelm API thread id is `{{thread_id}}`.
|
|
6
|
+
- Use this thread id when you need to correlate your own runtime actions with CompanyHelm thread state.
|
|
7
|
+
|
|
3
8
|
## Workspace Structure
|
|
4
9
|
|
|
5
10
|
- You are running in a thread-specific container and workspace.
|
|
@@ -14,22 +19,8 @@ Docker is available, the docker host runs in a separate container. The network i
|
|
|
14
19
|
- Nested DinD is not supported in this environment because the outer runtime already uses rootless DinD.
|
|
15
20
|
- If you need Docker in Docker, mount the configured host runtime instead of trying nested DinD.
|
|
16
21
|
|
|
17
|
-
## GitHub Installations
|
|
18
|
-
|
|
19
|
-
- Synced GitHub installation credentials are stored at `{{home_directory}}/.companyhelm/agent/installations.json`.
|
|
20
|
-
- Use `list-installations` to inspect installation IDs, repository scopes, tokens, and expiration timestamps.
|
|
21
|
-
- Use `gh-use-installation <installation-id>` to configure `gh` authentication for a specific installation.
|
|
22
|
-
|
|
23
|
-
```bash
|
|
24
|
-
list-installations
|
|
25
|
-
gh-use-installation {installation_id}
|
|
26
|
-
gh auth status --hostname github.com
|
|
27
|
-
```
|
|
28
|
-
|
|
29
22
|
## Available CLI Tools
|
|
30
23
|
|
|
31
|
-
- `list-installations`: list synced GitHub installations with repositories, access tokens, and expirations.
|
|
32
|
-
- `gh-use-installation <installation-id>`: configure `gh` authentication for a selected GitHub installation token.
|
|
33
24
|
- `aws`: AWS CLI is pre-installed and available in `PATH`.
|
|
34
25
|
- For scripted PR creation and updates, use `gh pr create --body-file <path>` and `gh pr edit --body-file <path>`.
|
|
35
26
|
- Playwright CLI is already installed and available with Chromium pre-installed.
|
|
@@ -3,3 +3,4 @@
|
|
|
3
3
|
- This runtime is using a shared host workspace mounted directly at `/workspace`.
|
|
4
4
|
- Changes made in `/workspace` may be visible to multiple threads that use the same runner workspace path.
|
|
5
5
|
- Do not assume `/workspace` is isolated per thread.
|
|
6
|
+
- you need to use git worktrees to isolate your working directory from the shared workspace.
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.VitestReporterResolver = void 0;
|
|
4
|
+
class VitestReporterResolver {
|
|
5
|
+
constructor(options) {
|
|
6
|
+
this.stdoutIsTTY = options.stdoutIsTTY;
|
|
7
|
+
this.env = options.env ?? process.env;
|
|
8
|
+
}
|
|
9
|
+
resolve() {
|
|
10
|
+
const configured = this.env.COMPANYHELM_VITEST_REPORTER?.trim();
|
|
11
|
+
if (configured && configured.length > 0) {
|
|
12
|
+
return configured
|
|
13
|
+
.split(",")
|
|
14
|
+
.map((value) => value.trim())
|
|
15
|
+
.filter((value) => value.length > 0);
|
|
16
|
+
}
|
|
17
|
+
if (this.stdoutIsTTY) {
|
|
18
|
+
return ["basic"];
|
|
19
|
+
}
|
|
20
|
+
return undefined;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
exports.VitestReporterResolver = VitestReporterResolver;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@companyhelm/runner",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.2.0",
|
|
4
4
|
"description": "Run the CompanyHelm runner in fully isolated Docker sandboxes.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@bufbuild/protobuf": "^2.11.0",
|
|
34
34
|
"@clack/prompts": "^1.0.1",
|
|
35
|
-
"@companyhelm/protos": "^0.5.
|
|
35
|
+
"@companyhelm/protos": "^0.5.26",
|
|
36
36
|
"@grpc/grpc-js": "^1.14.3",
|
|
37
37
|
"@libsql/client": "^0.17.0",
|
|
38
38
|
"commander": "^14.0.0",
|