Merge remote-tracking branch 'origin/G14' into fix/code-agent-timeout-control-20260527004132
# Conflicts: # deploy/deploy.json
This commit is contained in:
@@ -5,12 +5,12 @@
|
||||
"profile": "dev",
|
||||
"namespace": "hwlab-dev",
|
||||
"endpoint": "http://74.48.78.17:16667",
|
||||
"commitId": "01de135",
|
||||
"commitId": "2be9f52",
|
||||
"artifactState": "published",
|
||||
"publish": {
|
||||
"ciPublished": true,
|
||||
"registryVerified": true,
|
||||
"provenance": "g14-ci-artifact:hwlab-g14-ci-poll-01de135ecd9a-collect",
|
||||
"provenance": "g14-ci-artifact:hwlab-g14-ci-poll-2be9f524eb9f-collect",
|
||||
"note": "Digest fields were copied from a successful DEV artifact publish report for this source commit."
|
||||
},
|
||||
"healthContract": {
|
||||
@@ -66,11 +66,11 @@
|
||||
"services": [
|
||||
{
|
||||
"serviceId": "hwlab-cloud-api",
|
||||
"commitId": "b59d5b0",
|
||||
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:b59d5b0",
|
||||
"imageTag": "b59d5b0",
|
||||
"digest": "sha256:f6ac338f9826917d348a3218034c08e8102ac59f79054fcf2c7a4dd0ebdb1c37",
|
||||
"publishState": "reused",
|
||||
"commitId": "2be9f52",
|
||||
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:2be9f52",
|
||||
"imageTag": "2be9f52",
|
||||
"digest": "sha256:d54e052b24f066a7be82143e084be9804bb11991be8006e8ba5872c2300cbbe8",
|
||||
"publishState": "published",
|
||||
"profile": "dev",
|
||||
"namespace": "hwlab-dev",
|
||||
"healthPath": "/health/live",
|
||||
@@ -79,33 +79,31 @@
|
||||
"artifactRequired": true,
|
||||
"artifactScope": "required",
|
||||
"notPublishedReason": null,
|
||||
"buildCreatedAt": "2026-05-26T17:27:38.302Z",
|
||||
"buildCreatedAt": "2026-05-27T00:02:55.991Z",
|
||||
"buildSource": null,
|
||||
"componentCommitId": "b59d5b055224a31f4263d6939a8c6cdbbe4c2f52",
|
||||
"componentInputHash": "7c94d028be9c1bbe090eee4c3d065467c532766365c10814c5f66f21ebc26c44",
|
||||
"componentCommitId": "a452c14983aa957edde155d65bd17e1990782a86",
|
||||
"componentInputHash": "80ebdf7bc543b37d937efc1b7e8765de9c6a1e1f3d2f3cbe1ee65052fc32d1fe",
|
||||
"dockerfileHash": "3f56efe5dd980bf2081e640a5dcbed1c9f814d58b5fcd8bcb70de29f57373800",
|
||||
"baseImageReference": "127.0.0.1:5000/hwlab/hwlab-node20-base:20-bookworm-slim",
|
||||
"baseImageDigest": null,
|
||||
"buildArgsHash": "31de87c01b0c617b550b82b7a65780fac56d03a3b8abb9301bf0096766f87237",
|
||||
"ciAffected": false,
|
||||
"ciAffected": true,
|
||||
"ciReason": [
|
||||
"docs-only-no-image-build"
|
||||
"component-path-changed"
|
||||
],
|
||||
"reuse": {
|
||||
"status": "ready",
|
||||
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:b59d5b0",
|
||||
"digest": "sha256:f6ac338f9826917d348a3218034c08e8102ac59f79054fcf2c7a4dd0ebdb1c37",
|
||||
"reusedFrom": "7c94d028be9c1bbe090eee4c3d065467c532766365c10814c5f66f21ebc26c44"
|
||||
"status": "not-reused",
|
||||
"reason": "component-affected"
|
||||
},
|
||||
"reusedFrom": "7c94d028be9c1bbe090eee4c3d065467c532766365c10814c5f66f21ebc26c44"
|
||||
},
|
||||
{
|
||||
"serviceId": "hwlab-cloud-web",
|
||||
"commitId": "b59d5b0",
|
||||
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:b59d5b0",
|
||||
"imageTag": "b59d5b0",
|
||||
"digest": "sha256:6e13ec598dea1ed0fc909b385b328d002b95870d84b6c5bf5872b2c879bf5233",
|
||||
"publishState": "reused",
|
||||
"commitId": "2be9f52",
|
||||
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:2be9f52",
|
||||
"imageTag": "2be9f52",
|
||||
"digest": "sha256:a5a2a65d05e83121a673afe809449961ddcf9d05cb64b0b98d199e1fd32a3630",
|
||||
"publishState": "published",
|
||||
"profile": "dev",
|
||||
"namespace": "hwlab-dev",
|
||||
"healthPath": "/health/live",
|
||||
@@ -114,23 +112,21 @@
|
||||
"artifactRequired": true,
|
||||
"artifactScope": "required",
|
||||
"notPublishedReason": null,
|
||||
"buildCreatedAt": "2026-05-26T17:27:38.552Z",
|
||||
"buildCreatedAt": "2026-05-27T00:02:58.045Z",
|
||||
"buildSource": null,
|
||||
"componentCommitId": "b59d5b055224a31f4263d6939a8c6cdbbe4c2f52",
|
||||
"componentInputHash": "6e466c55638cd947836723061be34745dc803db50d92365b3bb359f92adfaf34",
|
||||
"componentCommitId": "a452c14983aa957edde155d65bd17e1990782a86",
|
||||
"componentInputHash": "298b4e28acf400c768f481883d777f7f6fe10a5542ff426c37f9f676698831c9",
|
||||
"dockerfileHash": "3f56efe5dd980bf2081e640a5dcbed1c9f814d58b5fcd8bcb70de29f57373800",
|
||||
"baseImageReference": "127.0.0.1:5000/hwlab/hwlab-node20-base:20-bookworm-slim",
|
||||
"baseImageDigest": null,
|
||||
"buildArgsHash": "39effb18af665e5e286cca9f84ef2a30f577ad11c9acca098589b7ee37f6950c",
|
||||
"ciAffected": false,
|
||||
"ciAffected": true,
|
||||
"ciReason": [
|
||||
"docs-only-no-image-build"
|
||||
"component-path-changed"
|
||||
],
|
||||
"reuse": {
|
||||
"status": "ready",
|
||||
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:b59d5b0",
|
||||
"digest": "sha256:6e13ec598dea1ed0fc909b385b328d002b95870d84b6c5bf5872b2c879bf5233",
|
||||
"reusedFrom": "6e466c55638cd947836723061be34745dc803db50d92365b3bb359f92adfaf34"
|
||||
"status": "not-reused",
|
||||
"reason": "component-affected"
|
||||
},
|
||||
"reusedFrom": "6e466c55638cd947836723061be34745dc803db50d92365b3bb359f92adfaf34"
|
||||
},
|
||||
@@ -159,7 +155,7 @@
|
||||
"buildArgsHash": "804a2e2f48cae4f739c3e88bb0d961359fca0a8e1b8cac43517fb065989df72b",
|
||||
"ciAffected": false,
|
||||
"ciReason": [
|
||||
"docs-only-no-image-build"
|
||||
"component-inputs-unchanged"
|
||||
],
|
||||
"reuse": {
|
||||
"status": "ready",
|
||||
@@ -194,7 +190,7 @@
|
||||
"buildArgsHash": "d5ea384583ca6916b9fdf4469a8c591e33e6e1a0ba9a59b9901fbaa797824cab",
|
||||
"ciAffected": false,
|
||||
"ciReason": [
|
||||
"docs-only-no-image-build"
|
||||
"component-inputs-unchanged"
|
||||
],
|
||||
"reuse": {
|
||||
"status": "ready",
|
||||
@@ -229,7 +225,7 @@
|
||||
"buildArgsHash": "083cc565bccb315303df802dfc1f7fb87738e763afb9662fed7bc7c12719b070",
|
||||
"ciAffected": false,
|
||||
"ciReason": [
|
||||
"docs-only-no-image-build"
|
||||
"component-inputs-unchanged"
|
||||
],
|
||||
"reuse": {
|
||||
"status": "ready",
|
||||
@@ -264,7 +260,7 @@
|
||||
"buildArgsHash": "79c757588262d875c61a2542e37c9a849000d54d8bb58e8bf19c597114956f5f",
|
||||
"ciAffected": false,
|
||||
"ciReason": [
|
||||
"docs-only-no-image-build"
|
||||
"component-inputs-unchanged"
|
||||
],
|
||||
"reuse": {
|
||||
"status": "ready",
|
||||
@@ -299,7 +295,7 @@
|
||||
"buildArgsHash": "2b321eec447decb8ca22b30a42dffa467405af983aa06e487b66a45c6ecbd0e9",
|
||||
"ciAffected": false,
|
||||
"ciReason": [
|
||||
"docs-only-no-image-build"
|
||||
"component-inputs-unchanged"
|
||||
],
|
||||
"reuse": {
|
||||
"status": "ready",
|
||||
@@ -334,7 +330,7 @@
|
||||
"buildArgsHash": "bea7ca0cf1e6f84b11a7848e3dff6dfe4aa613f1df2c51e9ab4807fffe718128",
|
||||
"ciAffected": false,
|
||||
"ciReason": [
|
||||
"docs-only-no-image-build"
|
||||
"component-inputs-unchanged"
|
||||
],
|
||||
"reuse": {
|
||||
"status": "ready",
|
||||
@@ -369,7 +365,7 @@
|
||||
"buildArgsHash": "31f0f27ef0e4375ac141f0d54c2c9449bd5dce14df0bb6469704245d1cf86b09",
|
||||
"ciAffected": false,
|
||||
"ciReason": [
|
||||
"docs-only-no-image-build"
|
||||
"component-inputs-unchanged"
|
||||
],
|
||||
"reuse": {
|
||||
"status": "ready",
|
||||
@@ -404,7 +400,7 @@
|
||||
"buildArgsHash": "c49a07574a1d4fe74642170efcbb231c2c59436e1d3d2434c285349d23a35fcb",
|
||||
"ciAffected": false,
|
||||
"ciReason": [
|
||||
"docs-only-no-image-build"
|
||||
"component-inputs-unchanged"
|
||||
],
|
||||
"reuse": {
|
||||
"status": "ready",
|
||||
@@ -439,7 +435,7 @@
|
||||
"buildArgsHash": "68746696603227763e5768f8daf45d31331f4c8425e6d756b21482ad104e6183",
|
||||
"ciAffected": false,
|
||||
"ciReason": [
|
||||
"docs-only-no-image-build"
|
||||
"component-inputs-unchanged"
|
||||
],
|
||||
"reuse": {
|
||||
"status": "ready",
|
||||
@@ -474,7 +470,7 @@
|
||||
"buildArgsHash": "bd9fa0f62fb873c87cf8ec47790d26c5068956c14be61eed222f78dc9f7f5c1a",
|
||||
"ciAffected": false,
|
||||
"ciReason": [
|
||||
"docs-only-no-image-build"
|
||||
"component-inputs-unchanged"
|
||||
],
|
||||
"reuse": {
|
||||
"status": "ready",
|
||||
@@ -509,7 +505,7 @@
|
||||
"buildArgsHash": "d5e50a18e70c6a8b4896e527fa07b5dd2c85458ed9ef512c1d46775ef4e40a10",
|
||||
"ciAffected": false,
|
||||
"ciReason": [
|
||||
"docs-only-no-image-build"
|
||||
"component-inputs-unchanged"
|
||||
],
|
||||
"reuse": {
|
||||
"status": "ready",
|
||||
|
||||
+9
-9
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"manifestVersion": "v1",
|
||||
"environment": "dev",
|
||||
"commitId": "01de135",
|
||||
"commitId": "2be9f52",
|
||||
"namespace": "hwlab-dev",
|
||||
"endpoint": "http://74.48.78.17:16667",
|
||||
"health": {
|
||||
@@ -181,7 +181,7 @@
|
||||
"services": [
|
||||
{
|
||||
"serviceId": "hwlab-cloud-api",
|
||||
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:b59d5b0",
|
||||
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:2be9f52",
|
||||
"namespace": "hwlab-dev",
|
||||
"healthPath": "/health/live",
|
||||
"profile": "dev",
|
||||
@@ -190,9 +190,9 @@
|
||||
"HWLAB_ENVIRONMENT": "dev",
|
||||
"HWLAB_PUBLIC_ENDPOINT": "http://74.48.78.17:16667",
|
||||
"HWLAB_CLOUD_API_PORT": "6667",
|
||||
"HWLAB_COMMIT_ID": "b59d5b0",
|
||||
"HWLAB_IMAGE": "127.0.0.1:5000/hwlab/hwlab-cloud-api:b59d5b0",
|
||||
"HWLAB_IMAGE_TAG": "b59d5b0",
|
||||
"HWLAB_COMMIT_ID": "2be9f52",
|
||||
"HWLAB_IMAGE": "127.0.0.1:5000/hwlab/hwlab-cloud-api:2be9f52",
|
||||
"HWLAB_IMAGE_TAG": "2be9f52",
|
||||
"HWLAB_RUNTIME_SUBSTITUTE_FORBIDDEN": "unidesk-backend,provider-gateway,microservice-proxy",
|
||||
"HWLAB_CLOUD_DB_URL": "secretRef:hwlab-cloud-api-dev-db/database-url",
|
||||
"HWLAB_CLOUD_DB_SSL_MODE": "disable",
|
||||
@@ -230,7 +230,7 @@
|
||||
},
|
||||
{
|
||||
"serviceId": "hwlab-cloud-web",
|
||||
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:b59d5b0",
|
||||
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:2be9f52",
|
||||
"namespace": "hwlab-dev",
|
||||
"healthPath": "/health/live",
|
||||
"profile": "dev",
|
||||
@@ -239,9 +239,9 @@
|
||||
"HWLAB_ENVIRONMENT": "dev",
|
||||
"HWLAB_API_BASE_URL": "http://hwlab-cloud-api.hwlab-dev.svc.cluster.local:6667",
|
||||
"HWLAB_CLOUD_WEB_PROXY_TIMEOUT_MS": "1260000",
|
||||
"HWLAB_COMMIT_ID": "b59d5b0",
|
||||
"HWLAB_IMAGE": "127.0.0.1:5000/hwlab/hwlab-cloud-web:b59d5b0",
|
||||
"HWLAB_IMAGE_TAG": "b59d5b0"
|
||||
"HWLAB_COMMIT_ID": "2be9f52",
|
||||
"HWLAB_IMAGE": "127.0.0.1:5000/hwlab/hwlab-cloud-web:2be9f52",
|
||||
"HWLAB_IMAGE_TAG": "2be9f52"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -31,7 +31,7 @@
|
||||
"containers": [
|
||||
{
|
||||
"name": "hwlab-cloud-api",
|
||||
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:b59d5b0",
|
||||
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:2be9f52",
|
||||
"ports": [
|
||||
{
|
||||
"name": "http",
|
||||
@@ -53,15 +53,15 @@
|
||||
},
|
||||
{
|
||||
"name": "HWLAB_COMMIT_ID",
|
||||
"value": "b59d5b0"
|
||||
"value": "2be9f52"
|
||||
},
|
||||
{
|
||||
"name": "HWLAB_IMAGE",
|
||||
"value": "127.0.0.1:5000/hwlab/hwlab-cloud-api:b59d5b0"
|
||||
"value": "127.0.0.1:5000/hwlab/hwlab-cloud-api:2be9f52"
|
||||
},
|
||||
{
|
||||
"name": "HWLAB_IMAGE_TAG",
|
||||
"value": "b59d5b0"
|
||||
"value": "2be9f52"
|
||||
},
|
||||
{
|
||||
"name": "HWLAB_RUNTIME_SUBSTITUTE_FORBIDDEN",
|
||||
@@ -245,7 +245,7 @@
|
||||
},
|
||||
{
|
||||
"name": "hwlab-codex-api-forwarder",
|
||||
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:b59d5b0",
|
||||
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-api:2be9f52",
|
||||
"command": [
|
||||
"node",
|
||||
"/app/cmd/hwlab-codex-api-responses-forwarder/main.mjs"
|
||||
@@ -367,7 +367,7 @@
|
||||
"containers": [
|
||||
{
|
||||
"name": "hwlab-cloud-web",
|
||||
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:b59d5b0",
|
||||
"image": "127.0.0.1:5000/hwlab/hwlab-cloud-web:2be9f52",
|
||||
"ports": [
|
||||
{
|
||||
"name": "http",
|
||||
@@ -385,15 +385,15 @@
|
||||
},
|
||||
{
|
||||
"name": "HWLAB_COMMIT_ID",
|
||||
"value": "b59d5b0"
|
||||
"value": "2be9f52"
|
||||
},
|
||||
{
|
||||
"name": "HWLAB_IMAGE",
|
||||
"value": "127.0.0.1:5000/hwlab/hwlab-cloud-web:b59d5b0"
|
||||
"value": "127.0.0.1:5000/hwlab/hwlab-cloud-web:2be9f52"
|
||||
},
|
||||
{
|
||||
"name": "HWLAB_IMAGE_TAG",
|
||||
"value": "b59d5b0"
|
||||
"value": "2be9f52"
|
||||
}
|
||||
],
|
||||
"readinessProbe": {
|
||||
|
||||
@@ -1057,7 +1057,7 @@ function codeAgentBlockedPayloadFromAcquire(acquireResult, { conversationId, tra
|
||||
};
|
||||
}
|
||||
|
||||
function createTimedFakeAppServerClient({ calls, text = "timed stdio reply", eventEveryMs = 20, eventCount = 0, completeAfterMs = null } = {}) {
|
||||
function createTimedFakeAppServerClient({ calls, text = "timed stdio reply", eventEveryMs = 20, eventCount = 0, completeAfterMs = null, commandExecution = null } = {}) {
|
||||
let notificationHandler = null;
|
||||
let turn = 0;
|
||||
const timers = new Set();
|
||||
@@ -1099,6 +1099,19 @@ function createTimedFakeAppServerClient({ calls, text = "timed stdio reply", eve
|
||||
});
|
||||
});
|
||||
}
|
||||
if (commandExecution) {
|
||||
const commandItem = {
|
||||
id: commandExecution.id ?? "cmd_stdio_timed_1",
|
||||
type: "commandExecution",
|
||||
...commandExecution
|
||||
};
|
||||
later(Math.max(1, Math.floor(eventEveryMs / 2)), () => {
|
||||
notificationHandler?.({ method: "item/started", params: { item: { ...commandItem, status: "running" } } });
|
||||
});
|
||||
later(eventEveryMs, () => {
|
||||
notificationHandler?.({ method: "item/completed", params: { item: { ...commandItem, status: commandExecution.status ?? "completed" } } });
|
||||
});
|
||||
}
|
||||
if (Number.isInteger(completeAfterMs)) {
|
||||
later(completeAfterMs, () => {
|
||||
notificationHandler?.({ method: "item/agentMessage/delta", params: { itemId: `item_${turn}`, delta: text } });
|
||||
@@ -1552,7 +1565,14 @@ test("Codex app-server activity timeout does not fail an active turn", async ()
|
||||
text: "active turn completed",
|
||||
eventEveryMs: 20,
|
||||
eventCount: 5,
|
||||
completeAfterMs: 130
|
||||
completeAfterMs: 130,
|
||||
commandExecution: {
|
||||
id: "cmd_stdio_trace_summary",
|
||||
command: ["tran_cmd", "gws_DESKTOP:/f/work/constart", "dir"],
|
||||
exitCode: 0,
|
||||
durationMs: 456,
|
||||
aggregatedOutput: "FREQ_Controller_FW.uvprojx\nkeil\n"
|
||||
}
|
||||
})
|
||||
});
|
||||
const env = {
|
||||
@@ -1590,6 +1610,13 @@ test("Codex app-server activity timeout does not fail an active turn", async ()
|
||||
assert.equal(payload.reply.content, "active turn completed");
|
||||
assert.ok(Date.now() - started >= 100);
|
||||
assert.ok(payload.runnerTrace.events.some((event) => event.label === "item/commandExecution/outputDelta"));
|
||||
const commandEvent = payload.runnerTrace.events.find((event) => event.label === "item/commandExecution:completed");
|
||||
assert.ok(commandEvent, JSON.stringify(payload.runnerTrace.events));
|
||||
assert.equal(commandEvent.toolName, "commandExecution");
|
||||
assert.equal(commandEvent.command, "tran_cmd gws_DESKTOP:/f/work/constart dir");
|
||||
assert.equal(commandEvent.exitCode, 0);
|
||||
assert.equal(commandEvent.durationMs, 456);
|
||||
assert.match(commandEvent.stdoutSummary, /FREQ_Controller_FW\.uvprojx/);
|
||||
assert.equal(payload.runnerTrace.events.some((event) => event.label === "timeout:no_activity"), false);
|
||||
assert.equal(payload.error, undefined);
|
||||
} finally {
|
||||
|
||||
@@ -322,8 +322,15 @@ function normalizeTraceEvent(event, { traceId, seq, now, fallbackRunnerKind } =
|
||||
sessionReused: typeof event.sessionReused === "boolean" ? event.sessionReused : undefined,
|
||||
turn: typeof event.turn === "number" ? event.turn : undefined,
|
||||
toolName,
|
||||
itemId: safeText(event.itemId, 180),
|
||||
command: safeText(event.command, 900),
|
||||
exitCode: Number.isInteger(event.exitCode) ? event.exitCode : undefined,
|
||||
durationMs: typeof event.durationMs === "number" ? Math.max(0, Math.trunc(event.durationMs)) : undefined,
|
||||
outputBytes: typeof event.outputBytes === "number" ? Math.max(0, Math.trunc(event.outputBytes)) : undefined,
|
||||
promptSummary: safeText(event.promptSummary, 240),
|
||||
outputSummary: safeText(event.outputSummary, 400),
|
||||
stdoutSummary: safeText(event.stdoutSummary, 600),
|
||||
stderrSummary: safeText(event.stderrSummary, 600),
|
||||
chunk: safeText(event.chunk, 400),
|
||||
message: safeText(event.message, 500),
|
||||
errorCode: safeText(event.errorCode, 120),
|
||||
|
||||
@@ -1731,6 +1731,20 @@ function createAppServerTurnState({ traceRecorder, session } = {}) {
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (method === "item/started" && item?.type === "commandExecution") {
|
||||
appendCommandExecutionTrace(item, {
|
||||
status: "started",
|
||||
label: "item/commandExecution:started"
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (method === "item/completed" && item?.type === "commandExecution") {
|
||||
appendCommandExecutionTrace(item, {
|
||||
status: appServerCommandStatus(item),
|
||||
label: "item/commandExecution:completed"
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (method === "item/commandExecution/outputDelta" || method === "item/reasoning/summaryTextDelta" || method === "item/reasoning/textDelta") {
|
||||
appendTrace({
|
||||
type: method.startsWith("item/reasoning/") ? "reasoning" : "tool_call",
|
||||
@@ -1790,6 +1804,30 @@ function createAppServerTurnState({ traceRecorder, session } = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
function appendCommandExecutionTrace(item, { status, label }) {
|
||||
const output = commandExecutionOutputText(item);
|
||||
appendTrace({
|
||||
type: "tool_call",
|
||||
stage: "tool_call",
|
||||
status,
|
||||
label,
|
||||
itemId: optionalId(item.id),
|
||||
toolName: "commandExecution",
|
||||
command: redactText(commandExecutionCommandText(item)),
|
||||
exitCode: Number.isInteger(item.exitCode) ? item.exitCode : undefined,
|
||||
durationMs: typeof item.durationMs === "number" ? item.durationMs : undefined,
|
||||
outputBytes: output.length,
|
||||
stdoutSummary: output ? tailText(redactText(output), 600) : undefined,
|
||||
stderrSummary: commandExecutionErrorText(item),
|
||||
sessionId: session?.sessionId,
|
||||
sessionStatus: session?.status,
|
||||
turn: session?.turn,
|
||||
threadId,
|
||||
turnId,
|
||||
waitingFor: status === "completed" ? "turn/completed" : "commandExecution/completed"
|
||||
});
|
||||
}
|
||||
|
||||
async function wait(timeoutMs, closedPromise, { hardTimeoutMs = null } = {}) {
|
||||
let activityTimer;
|
||||
let hardTimer;
|
||||
@@ -2017,6 +2055,58 @@ function appServerTerminalStatus(value) {
|
||||
return raw ? "failed" : null;
|
||||
}
|
||||
|
||||
function appServerCommandStatus(item) {
|
||||
const raw = String(item?.status ?? "").trim().toLowerCase();
|
||||
if (raw === "completed" || raw === "complete" || raw === "success" || raw === "succeeded") return "completed";
|
||||
if (raw === "failed" || raw === "error") return "failed";
|
||||
if (Number.isInteger(item?.exitCode)) return item.exitCode === 0 ? "completed" : "failed";
|
||||
return raw || "completed";
|
||||
}
|
||||
|
||||
function commandExecutionCommandText(item) {
|
||||
return firstCommandExecutionText(
|
||||
item?.command,
|
||||
item?.cmd,
|
||||
item?.argv,
|
||||
item?.input,
|
||||
item?.metadata?.command
|
||||
);
|
||||
}
|
||||
|
||||
function commandExecutionOutputText(item) {
|
||||
return firstCommandExecutionText(
|
||||
item?.aggregatedOutput,
|
||||
item?.output,
|
||||
item?.stdout,
|
||||
item?.result,
|
||||
item?.metadata?.output
|
||||
);
|
||||
}
|
||||
|
||||
function commandExecutionErrorText(item) {
|
||||
const error = item?.error;
|
||||
const message = typeof error === "string" ? error : typeof error?.message === "string" ? error.message : "";
|
||||
const text = firstCommandExecutionText(item?.stderr, message, item?.metadata?.stderr);
|
||||
return text ? tailText(redactText(text), 600) : undefined;
|
||||
}
|
||||
|
||||
function firstCommandExecutionText(...values) {
|
||||
for (const value of values) {
|
||||
const text = commandExecutionTextValue(value);
|
||||
if (text) return text;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
function commandExecutionTextValue(value) {
|
||||
if (typeof value === "string") return value.trim();
|
||||
if (Array.isArray(value)) return value.map((item) => commandExecutionTextValue(item)).filter(Boolean).join(" ").trim();
|
||||
if (value && typeof value === "object") {
|
||||
return firstCommandExecutionText(value.text, value.content, value.message, value.stdout, value.stderr, value.command, value.cmd);
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
export function longLivedSessionGate({
|
||||
provider,
|
||||
runnerKind,
|
||||
|
||||
@@ -5058,6 +5058,7 @@ function compactTraceTextTail(text, maxLength) {
|
||||
|
||||
function traceDisplayRow(trace, event, options = {}) {
|
||||
if (!options.includeNoise && isNoisyTraceEvent(event)) return null;
|
||||
if (isCommandExecutionTraceEvent(event)) return traceCommandExecutionRow(trace, event);
|
||||
const clock = traceClock(event.createdAt);
|
||||
const total = formatTraceDuration(traceRelativeMs(trace, event));
|
||||
const status = traceStatusToken(event);
|
||||
@@ -5078,6 +5079,43 @@ function traceDisplayRow(trace, event, options = {}) {
|
||||
};
|
||||
}
|
||||
|
||||
function traceCommandExecutionRow(trace, event) {
|
||||
const clock = traceClock(event.createdAt);
|
||||
const total = formatTraceDuration(traceRelativeMs(trace, event));
|
||||
const status = traceStatusToken(event);
|
||||
const command = cleanTraceOutputText(event.command ?? "");
|
||||
const meta = [
|
||||
status,
|
||||
"tool cmd",
|
||||
command ? `:: ${compactTraceTextTail(command, 220)}` : null,
|
||||
Number.isInteger(event.exitCode) ? `exit=${event.exitCode}` : null,
|
||||
typeof event.durationMs === "number" ? `s=${(event.durationMs / 1000).toFixed(1)}` : null,
|
||||
typeof event.outputBytes === "number" ? `out=${formatTraceBytes(event.outputBytes)}` : null,
|
||||
event.itemId ? `item=${event.itemId}` : null
|
||||
].filter(Boolean).join(" ");
|
||||
return {
|
||||
seq: event.seq ?? null,
|
||||
tone: traceEventTone(event),
|
||||
header: `${clock} total=${total} ${meta}`,
|
||||
body: traceCommandExecutionBody(event)
|
||||
};
|
||||
}
|
||||
|
||||
function traceCommandExecutionBody(event) {
|
||||
const lines = [
|
||||
event.command ? `command=${compactTraceTextTail(cleanTraceOutputText(event.command), 900)}` : null,
|
||||
event.stdoutSummary ? `stdout:\n${compactTraceTextTail(cleanTraceOutputText(event.stdoutSummary), 1200)}` : null,
|
||||
event.stderrSummary ? `stderr:\n${compactTraceTextTail(cleanTraceOutputText(event.stderrSummary), 800)}` : null,
|
||||
event.waitingFor && event.status !== "completed" ? `waiting=${event.waitingFor}` : null
|
||||
].filter(Boolean);
|
||||
return lines.join("\n");
|
||||
}
|
||||
|
||||
function isCommandExecutionTraceEvent(event) {
|
||||
return event?.type === "tool_call" && event?.toolName === "commandExecution" &&
|
||||
(event?.label === "item/commandExecution:started" || event?.label === "item/commandExecution:completed");
|
||||
}
|
||||
|
||||
function isNoisyTraceEvent(event) {
|
||||
const label = String(event?.label ?? "");
|
||||
if (isToolOutputChunkTraceEvent(event)) return false;
|
||||
@@ -5106,6 +5144,8 @@ function readableTraceLabel(event) {
|
||||
if (label === "request:accepted") return "request accepted";
|
||||
if (label.startsWith("tool:codex-app-server.thread/start+turn/start")) return "codex turn start";
|
||||
if (label.startsWith("tool:codex-app-server.thread/resume+turn/start")) return "codex turn resume";
|
||||
if (label === "item/commandExecution:started") return "cmd started";
|
||||
if (label === "item/commandExecution:completed") return "cmd completed";
|
||||
if (label.startsWith("item/commandExecution/outputDelta")) return "cmd output";
|
||||
if (label === "turn:waiting:first_assistant_token") return "waiting first assistant token";
|
||||
if (label.startsWith("turn:completed")) return "turn completed";
|
||||
@@ -5276,6 +5316,13 @@ function compactTraceSize(value) {
|
||||
return String(length);
|
||||
}
|
||||
|
||||
function formatTraceBytes(value) {
|
||||
const bytes = Math.max(0, Number(value) || 0);
|
||||
if (bytes >= 1024 * 1024) return `${(bytes / 1024 / 1024).toFixed(1)}MiB`;
|
||||
if (bytes >= 1024) return `${(bytes / 1024).toFixed(1)}KiB`;
|
||||
return `${bytes}B`;
|
||||
}
|
||||
|
||||
function cleanTraceText(value) {
|
||||
const text = String(value ?? "").trim();
|
||||
if (!text) return "";
|
||||
|
||||
@@ -160,6 +160,18 @@ test("trace display full means complete readable timeline, not compacted result
|
||||
status: "started",
|
||||
message: "accepted"
|
||||
},
|
||||
{
|
||||
label: "item/commandExecution:completed",
|
||||
type: "tool_call",
|
||||
status: "completed",
|
||||
toolName: "commandExecution",
|
||||
command: "node /app/tools/hwlab-gateway-shell.mjs --json --timeout-ms 120000",
|
||||
exitCode: 0,
|
||||
durationMs: 1234,
|
||||
outputBytes: 42,
|
||||
stdoutSummary: "gateway stdout ok",
|
||||
stderrSummary: ""
|
||||
},
|
||||
{
|
||||
label: "item/commandExecution/outputDelta",
|
||||
type: "tool_call",
|
||||
@@ -183,6 +195,9 @@ test("trace display full means complete readable timeline, not compacted result
|
||||
const replaying = await tracePanelText(page);
|
||||
assert.match(replaying.count, /完整 trace 回放中/);
|
||||
assert.doesNotMatch(replaying.count, /压缩窗口|显示全部/);
|
||||
assert.ok(replaying.rows.some((row) => row.includes("tool cmd :: node /app/tools/hwlab-gateway-shell.mjs --json") && row.includes("exit=0") && row.includes("s=1.2") && row.includes("out=42B")), replaying.rows.join("\n"));
|
||||
assert.doesNotMatch(replaying.rows.join("\n"), /node \/ app \/ tools/);
|
||||
assert.match(replaying.bodies.join("\n"), /stdout:\s*gateway stdout ok/);
|
||||
assert.equal(replaying.rows.filter((row) => row.includes("tool gateway.shell")).length, 2, replaying.rows.join("\n---\n"));
|
||||
assert.ok(replaying.rows.some((row) => row.includes("op=op_one")), replaying.rows.join("\n"));
|
||||
assert.ok(replaying.rows.some((row) => row.includes("op=op_two")), replaying.rows.join("\n"));
|
||||
|
||||
Reference in New Issue
Block a user