Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 14 additions & 0 deletions apps/server/src/orchestration-v2/ProviderSessionManager.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5641,3 +5641,17 @@ it.effect("ProviderSessionManagerV2 gives a delegated child its project's device
assert.isTrue(child?.capabilities?.has("device"));
}),
);

it("ProviderSessionManagerV2 reads its idle timeout from the host environment", () => {
const read = (value: string | undefined) =>
ProviderSessionManager.idleTimeoutFromEnvironment({
[ProviderSessionManager.IDLE_TIMEOUT_MINUTES_ENV]: value,
});
assert.isUndefined(read(undefined));
assert.isUndefined(read(" "));
assert.deepStrictEqual(read("90"), { idleTimeoutMs: 90 * 60 * 1000 });
assert.deepStrictEqual(read(" 1440 "), { idleTimeoutMs: 1440 * 60 * 1000 });
for (const value of ["0", "1441", "1.5", "-5", "30m", "1e3"]) {
assert.deepStrictEqual(read(value), { invalid: value }, value);
}
});
33 changes: 32 additions & 1 deletion apps/server/src/orchestration-v2/ProviderSessionManager.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { AgentScope } from "@t3tools/shared/AgentScope";
import * as HostProcess from "@t3tools/shared/HostProcess";
import * as KeyedLock from "@t3tools/shared/KeyedLock";
import { resolveProjectSettings } from "@t3tools/shared/projectSettings";
import {
Expand Down Expand Up @@ -68,6 +69,13 @@ import * as ProjectionStore from "./ProjectionStore.ts";

const DEFAULT_IDLE_TIMEOUT_MS = 30 * 60 * 1000;
const DEFAULT_MAX_IDLE_PIN_MS = 4 * 60 * 60 * 1000;
/**
* Lets a host keep idle provider processes longer than the default, for example so a
* prompt-cache keep-alive running inside the provider process outlives the release timer.
* Each idle process holds its memory until released, so the default stays unchanged.
*/
export const IDLE_TIMEOUT_MINUTES_ENV = "T3CODE_PROVIDER_IDLE_TIMEOUT_MINUTES";
const MAX_IDLE_TIMEOUT_MINUTES = 24 * 60;
const RELEASE_SCOPE_CLOSE_TIMEOUT_MS = 30 * 1000;
/** Past adapters' own 10 s stop fallbacks, so they settle first when the provider answers at all. */
export const PROVIDER_INTERRUPT_TIMEOUT_MS = 15 * 1000;
Expand Down Expand Up @@ -3424,4 +3432,27 @@ export const layerWithOptions = (
}),
);

export const layer = layerWithOptions();
/** `undefined` keeps the default; `invalid` is a value to warn about and ignore. */
export function idleTimeoutFromEnvironment(
env: NodeJS.ProcessEnv,
): { readonly idleTimeoutMs: number } | { readonly invalid: string } | undefined {
const raw = env[IDLE_TIMEOUT_MINUTES_ENV]?.trim();
if (raw === undefined || raw === "") return undefined;
const minutes = /^\d+$/.test(raw) ? Number(raw) : Number.NaN;
if (!(minutes >= 1 && minutes <= MAX_IDLE_TIMEOUT_MINUTES)) return { invalid: raw };
return { idleTimeoutMs: minutes * 60 * 1000 };
}

export const layer = Layer.unwrap(
Effect.gen(function* () {
const configured = idleTimeoutFromEnvironment(yield* HostProcess.Environment);
if (configured !== undefined && "invalid" in configured) {
yield* Effect.logWarning(
`Ignoring ${IDLE_TIMEOUT_MINUTES_ENV}: expected whole minutes from 1 to ${MAX_IDLE_TIMEOUT_MINUTES}`,
{ value: configured.invalid },
);
return layerWithOptions();
}
return layerWithOptions(configured);
}),
);
8 changes: 8 additions & 0 deletions docs/user/background-service.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,14 @@ A missing file is ignored; other read failures stop service startup.
If the file contains credentials, restrict access to that file with `chmod 600`
(for the default T3 home, `chmod 600 ~/.t3/service.env`).

T3 Code stops an agent's process after its thread has been idle for 30 minutes
and resumes it on the next message. To keep idle processes longer, for example
for a tool that keeps a provider's prompt cache warm from inside that process,
set `T3CODE_PROVIDER_IDLE_TIMEOUT_MINUTES` to a whole number of minutes from 1
to 1440. Every idle process keeps its memory until it stops, so a longer value
uses more memory when many threads sit idle. An invalid value is logged and
ignored.

## Platform support

Linux needs systemd user services. Setup enables lingering so T3 Code starts at
Expand Down
Loading