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
1 change: 1 addition & 0 deletions packages/coding-agent/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- Added privacy-safe pseudonymous product analytics for onboarding, command use, execution modes, run outcomes, TTFT, latency, usage, tools, retries, and compactions, with disclosure and opt-out controls ([ENG-4682](https://linear.app/primeintellect/issue/ENG-4682/add-privacy-safe-posthog-analytics-to-prime-agent)).
- Changed sent agent messages in the IPython cell UI to show only the message text with a `╰─` gutter when expanded, matching received messages, and hid the raw `agent_message.send` receipt dictionary.
- Fixed Homebrew installs attempting to self-update their versioned Cellar keg instead of directing users to `brew upgrade prime-agent` ([#844](https://github.com/PrimeIntellect-ai/prime-agent/issues/844))
- Fixed macOS timezone changes invalidating the running daemon's process identity ([#879](https://github.com/PrimeIntellect-ai/prime-agent/issues/879)).

## [0.7.1] - 2026-08-07

Expand Down
30 changes: 22 additions & 8 deletions packages/coding-agent/src/core/session-lease.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,12 +110,17 @@ function isProcessAlive(pid: number): boolean {
}
}

type ProcessQuery = (command: string, args: string[]) => string;
interface ProcessQueryOptions {
env?: NodeJS.ProcessEnv;
}

type ProcessQuery = (command: string, args: string[], options?: ProcessQueryOptions) => string;

function runProcessQuery(command: string, args: string[]): string {
function runProcessQuery(command: string, args: string[], options?: ProcessQueryOptions): string {
return execFileSync(command, args, {
encoding: "utf8",
stdio: ["ignore", "pipe", "ignore"],
env: options?.env,
});
}

Expand All @@ -137,6 +142,20 @@ export function getWindowsProcessStartId(pid: number, query: ProcessQuery = runP
}
}

export function getPsProcessStartId(pid: number, query: ProcessQuery = runProcessQuery): string | undefined {
if (!Number.isInteger(pid) || pid <= 0) {
return undefined;
}
try {
const startTime = query("ps", ["-p", String(pid), "-o", "lstart="], {
env: { ...process.env, TZ: "UTC", LC_ALL: "C" },
}).trim();
return startTime ? `ps:${startTime}` : undefined;
} catch {
return undefined;
}
}

export function getProcessStartId(pid: number): string | undefined {
if (!Number.isInteger(pid) || pid <= 0) {
return undefined;
Expand All @@ -155,12 +174,7 @@ export function getProcessStartId(pid: number): string | undefined {
} catch {
// Fall through to the portable process listing used on macOS and BSD.
}
try {
const startTime = runProcessQuery("ps", ["-p", String(pid), "-o", "lstart="]).trim();
return startTime ? `ps:${startTime}` : undefined;
} catch {
return undefined;
}
return getPsProcessStartId(pid);
}

let currentProcessStartId: string | undefined;
Expand Down
34 changes: 33 additions & 1 deletion packages/coding-agent/test/daemon-ps.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
sortDaemons,
verifyHelloSupervisorPid,
} from "../src/cli/daemon-ps.js";
import { getProcessStartId } from "../src/core/session-lease.js";
import { getProcessStartId, getPsProcessStartId } from "../src/core/session-lease.js";
import { defaultDaemonSocketDir } from "../src/modes/daemon/daemon-socket.js";

describe("worker socket classification", () => {
Expand Down Expand Up @@ -116,6 +116,38 @@ describe("verifyHelloSupervisorPid", () => {
});
});

describe("getPsProcessStartId", () => {
it("uses a stable timezone and locale for the portable process identity", () => {
let queryEnvironment: NodeJS.ProcessEnv | undefined;
const result = getPsProcessStartId(1234, (_command, _args, options) => {
queryEnvironment = options?.env;
return "Sat Aug 8 06:00:00 2026\n";
});

expect(result).toBe("ps:Sat Aug 8 06:00:00 2026");
expect(queryEnvironment).toMatchObject({ TZ: "UTC", LC_ALL: "C" });
});

it("preserves the surrounding environment", () => {
let queryEnvironment: NodeJS.ProcessEnv | undefined;
const previous = process.env.PRIME_AGENT_PROCESS_START_TEST;
process.env.PRIME_AGENT_PROCESS_START_TEST = "preserved";
try {
getPsProcessStartId(1234, (_command, _args, options) => {
queryEnvironment = options?.env;
return "Sat Aug 8 06:00:00 2026\n";
});
expect(queryEnvironment?.PRIME_AGENT_PROCESS_START_TEST).toBe("preserved");
} finally {
if (previous === undefined) {
delete process.env.PRIME_AGENT_PROCESS_START_TEST;
} else {
process.env.PRIME_AGENT_PROCESS_START_TEST = previous;
}
}
});
});

describe("parsePsEtimes", () => {
it("maps pid to elapsed seconds", () => {
const uptimes = parsePsEtimes(" 1234 86400\n 5678 42\n");
Expand Down