@lark-codem/codem-cli 0.1.252 → 0.1.253

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lark-codem/codem-cli",
3
- "version": "0.1.252",
3
+ "version": "0.1.253",
4
4
  "codemServiceManagementProtocol": 3,
5
5
  "codemProfileProtocol": 1,
6
6
  "type": "module",
@@ -29,17 +29,17 @@
29
29
  "access": "public"
30
30
  },
31
31
  "dependencies": {
32
- "@lark-codem/codem-core": "0.8.80",
32
+ "@lark-codem/codem-core": "0.8.79",
33
33
  "zod": "3.25.76",
34
- "@lark-codem/lark-cli": "0.1.1-test.20261024.3"
34
+ "@lark-codem/lark-cli": "0.1.1"
35
35
  },
36
36
  "optionalDependencies": {
37
- "@lark-codem/codem-cli-darwin-arm64": "0.1.252",
38
- "@lark-codem/codem-cli-darwin-x64": "0.1.252",
39
- "@lark-codem/codem-cli-linux-arm64": "0.1.252",
40
- "@lark-codem/codem-cli-linux-x64": "0.1.252",
41
- "@lark-codem/codem-cli-win32-arm64": "0.1.252",
42
- "@lark-codem/codem-cli-win32-x64": "0.1.252"
37
+ "@lark-codem/codem-cli-darwin-arm64": "0.1.253",
38
+ "@lark-codem/codem-cli-darwin-x64": "0.1.253",
39
+ "@lark-codem/codem-cli-linux-arm64": "0.1.253",
40
+ "@lark-codem/codem-cli-linux-x64": "0.1.253",
41
+ "@lark-codem/codem-cli-win32-arm64": "0.1.253",
42
+ "@lark-codem/codem-cli-win32-x64": "0.1.253"
43
43
  },
44
44
  "engines": {
45
45
  "node": ">=18"
@@ -30,5 +30,8 @@ export class CredentialStorageError extends Error {
30
30
 
31
31
  export function isRecoverableCredentialState(error) {
32
32
  return error?.code === "credential_storage_rotation_pending"
33
- || error?.code === "credential_storage_rotation_unknown";
33
+ || error?.code === "credential_storage_rotation_unknown"
34
+ // A missing OAuth record permits reauthorization with the existing device,
35
+ // never use of its stale plaintext mirror or another credential kind.
36
+ || (error?.code === "credential_storage_missing" && error.kind === "oauth");
34
37
  }
@@ -10,7 +10,28 @@ const CAPACITY = 256;
10
10
  const BATCH_SIZE = 32;
11
11
  const INTERVAL_MS = 10_000;
12
12
  const REQUEST_TIMEOUT_MS = 3_000;
13
+ const OBSERVATION_EVENTS = new Set(["command_delivery_observed", "command_admission", "runtime_capability_probe", "command_ack", "command_observation_closed", "diagnostic_reporter_health"]);
14
+ const OBSERVATION_DIMENSIONS = {
15
+ phase: ["resolve_binary", "build_env", "host_capabilities", "parse_capabilities", "strict_resume", "verify_binding", "prepare", "persist_state", "trusted_device", "request", "validate_response", "apply_response"],
16
+ reason: ["runtime_unavailable", "runtime_environment_failed", "capability_probe_failed", "runtime_binary_changed", "strict_resume_unsupported", "turn_context_unsupported", "active_run_protocol_mismatch", "trusted_device_blocked", "shutting_down_handoff", "handoff_target_not_active", "duplicate_inflight", "handler_inflight", "prepare_exception_unknown", "capacity", "stopped", "replaced", "scope_changed", "queue_removed", "local_rejected"],
17
+ command_type: ["agent.run", "agent.steer", "agent.stop", "task.assign", "task.stop", "artifact.relay.read", "artifact.convert_to_lark_resource", "artifact.download", "directory.list", "git.preview", "workflow.preview"],
18
+ actor_type: ["device", "sandbox"],
19
+ ack_status: ["accepted", "ignored_duplicate", "rejected"],
20
+ ack_outcome: ["accepted", "duplicate", "rejected", "conflicting"],
21
+ readback_scope: ["command", "delivery", "unmatched"],
22
+ timing_origin: ["observed", "restored_unknown"]
23
+ };
24
+ const OBSERVATION_METRICS = {
25
+ command_delivery_observed: ["attempt_delta", "suppressed_count"],
26
+ command_admission: ["attempt_delta", "suppressed_count", "deferred_age_ms", "deferred_duration_ms"],
27
+ runtime_capability_probe: ["probe_duration_ms"],
28
+ command_ack: ["attempt_delta", "suppressed_count", "ack_queue_wait_ms", "ack_settle_duration_ms", "pending_ack_age_ms", "pending_command_ack_count", "pending_ack_unknown_age_count"],
29
+ command_observation_closed: ["omitted_detail_count", "pending_command_ack_count", "deferred_count"],
30
+ diagnostic_reporter_health: ["dropped_delta", "upload_failure_delta", "pending_event_count"]
31
+ };
32
+ const EXTRA_FIELDS = ["observation_id", "runtime_client_instance_id", "command_id", "delivery_id", "probe_id", "sync_id", "transport_request_id"];
13
33
  const STRING_FIELDS = new Set([
34
+ ...EXTRA_FIELDS, "command_type", "ack_status", "ack_outcome", "readback_scope", "timing_origin", "required_capability",
14
35
  "request_id", "command_id", "session_id", "task_id", "dispatch_id", "event_id", "sync_id",
15
36
  "process_instance_id", "run_id", "stage", "phase", "status", "outcome", "reason", "action",
16
37
  "error_class", "error_code", "error_kind", "error_type", "code", "signal", "stop_reason",
@@ -19,6 +40,7 @@ const STRING_FIELDS = new Set([
19
40
  "failure_kind", "failure_impact", "failure_type", "failure_subtype", "failure_code", "failure_status", "delay_source", "mode", "provider", "name", "termination", "interaction_id", "interaction_kind", "owner_state", "artifact_id", "resource_type", "delivery_mode", "host", "scheme", "tool_call_id", "tool_name", "background_task_id", "model", "artifact_kind", "codem_user_id", "device_id", "operation", "foreground_error_code"
20
41
  ]);
21
42
  const VALUE_FIELDS = new Set([
43
+ ...new Set(Object.values(OBSERVATION_METRICS).flat()),
22
44
  "pid", "exit_code", "duration_ms", "elapsed_ms", "wait_ms", "run_ms", "total_ms", "took_ms",
23
45
  "attempt", "attempts", "attempt_count", "retryable", "http_status", "active_task_count",
24
46
  "pending_event_count", "pending_command_ack_count", "next_retry_ms", "consecutive_failures",
@@ -40,6 +62,16 @@ function optional(read) {
40
62
  try { return read(); } catch { return undefined; }
41
63
  }
42
64
 
65
+ function metricProjection(event, fields = {}) {
66
+ return { count: 1, ...Object.fromEntries((OBSERVATION_METRICS[event] ?? [])
67
+ .filter(key => typeof fields?.[key] === "number" && Number.isFinite(fields[key]) && fields[key] >= 0)
68
+ .map(key => [key, fields[key]])) };
69
+ }
70
+
71
+ function extraProjection(fields = {}) {
72
+ return Object.fromEntries(EXTRA_FIELDS.filter(key => token(fields?.[key])).map(key => [key, token(fields[key])]));
73
+ }
74
+
43
75
  function projectFields(input) {
44
76
  const result = {};
45
77
  for (const [key, value] of Object.entries(input ?? {}).slice(0, 100)) {
@@ -52,6 +84,8 @@ function projectFields(input) {
52
84
  }
53
85
  }
54
86
  const error = input?.error ?? input?.terminalError;
87
+ const fingerprint = input?.error_fingerprint ?? input?.errorFingerprint;
88
+ if (typeof fingerprint === "string" && /^[a-f0-9]{24}$/u.test(fingerprint)) result.error_fingerprint = fingerprint;
55
89
  const terminal = input?.terminalError ?? error?.terminalError;
56
90
  if (terminal && typeof terminal === "object") {
57
91
  if (token(terminal.code)) result.error_code = token(terminal.code);
@@ -90,6 +124,10 @@ export class RuntimeLogReporter {
90
124
  #timer;
91
125
  #closed = false;
92
126
  #dropped = 0;
127
+ #uploadFailures = 0;
128
+ #reportedDrops = 0;
129
+ #reportedFailures = 0;
130
+ #lastHealthAt = Date.now();
93
131
  #send;
94
132
  #role;
95
133
  #version;
@@ -144,7 +182,7 @@ export class RuntimeLogReporter {
144
182
  if (fs.lstatSync(file).isSymbolicLink() || fs.statSync(file).size > 65536) continue;
145
183
  const entry = JSON.parse(fs.readFileSync(file, "utf8"));
146
184
  if (entry?.record?.category?.reporter !== "codem_js" || entry?.record?.log_type !== "log" || !entry.header) continue;
147
- this.#replay.queue.push({ header: entry.header, record: entry.record, attempts: 0, crashFile: file });
185
+ this.#replay.queue.push({ header: entry.header, record: entry.record, metrics: metricProjection(entry.record.category.event, entry.metrics), extra: extraProjection(entry.extra), attempts: 0, crashFile: file });
148
186
  } catch { /* A partial crash write cannot prevent startup. */ }
149
187
  }
150
188
  } catch { /* First startup or unavailable storage. */ }
@@ -169,12 +207,14 @@ export class RuntimeLogReporter {
169
207
 
170
208
  get pendingCount() { return this.#live.queue.length + this.#replay.queue.length; }
171
209
  get droppedCount() { return this.#dropped; }
210
+ get identityKey() { return JSON.stringify([this.#instanceId, this.#role, this.#identity.deviceId, this.#identity.userId, this.#identity.tenantId]); }
172
211
 
173
212
  record(level, component, event, fields = {}) {
174
213
  try {
175
214
  if (this.#closed) return false;
176
215
  let safeFields;
177
216
  try { safeFields = projectFields(fields); } catch { safeFields = {}; }
217
+ if (OBSERVATION_EVENTS.has(event)) safeFields.observation_id = randomUUID();
178
218
  const severity = ["info", "warn", "error", "fatal"].includes(level) ? level : "info";
179
219
  const identity = { ...this.#identity };
180
220
  const category = {
@@ -186,6 +226,11 @@ export class RuntimeLogReporter {
186
226
  ...(identity.serverEnvironment ? { server_environment: identity.serverEnvironment } : {}),
187
227
  ...Object.fromEntries(["stage", "error_class", "error_code", "error_kind", "outcome"].filter((key) => safeFields[key]).map((key) => [key, safeFields[key]]))
188
228
  };
229
+ if (OBSERVATION_EVENTS.has(category.event)) {
230
+ for (const [key, values] of Object.entries(OBSERVATION_DIMENSIONS)) {
231
+ if (safeFields[key]) category[key] = values.includes(safeFields[key]) ? safeFields[key] : "unknown";
232
+ }
233
+ }
189
234
  const header = {
190
235
  aid: SLARDAR_PC_AID,
191
236
  device_id: identity.deviceId ?? this.#instanceId,
@@ -207,17 +252,39 @@ export class RuntimeLogReporter {
207
252
  log_type: "log", log_level: severity === "fatal" ? "error" : severity,
208
253
  log_content: `CodeM diagnostic ${severity} ${category.component}/${category.event}\n${JSON.stringify(payload)}`
209
254
  };
255
+ const priority = severity === "fatal" ? 3 :
256
+ ["resumed", "settled"].includes(safeFields.stage) || (severity === "error" && !safeFields.suppressed_count) ? 2 :
257
+ severity === "info" ? 0 : 1;
210
258
  if (this.#live.queue.length >= CAPACITY) {
211
- const expendable = this.#live.queue.findIndex((entry) => entry.record.log_level === "info");
212
- this.#live.queue.splice(expendable >= 0 ? expendable : 0, 1);
259
+ const minimum = Math.min(...this.#live.queue.map(entry => entry.priority ?? (entry.record.log_level === "info" ? 0 : 1)));
260
+ if (minimum > priority) { this.#dropped += 1; return false; }
261
+ const expendable = this.#live.queue.findIndex(entry => (entry.priority ?? (entry.record.log_level === "info" ? 0 : 1)) === minimum);
262
+ this.#live.queue.splice(expendable, 1);
213
263
  this.#dropped += 1;
214
264
  }
215
- this.#live.queue.push({ header, record, attempts: 0 });
265
+ this.#live.queue.push({ header, record, metrics: metricProjection(category.event, safeFields), extra: extraProjection(safeFields), priority, attempts: 0 });
216
266
  return true;
217
267
  } catch { return false; }
218
268
  }
219
269
 
220
270
  async flush({ timeoutMs = REQUEST_TIMEOUT_MS } = {}) {
271
+ // Counters only in the failure path: never recursively log an upload error.
272
+ if (Date.now() - this.#lastHealthAt >= 60_000 && (this.#dropped !== this.#reportedDrops || this.#uploadFailures !== this.#reportedFailures)) {
273
+ this.#lastHealthAt = Date.now();
274
+ const dropped = this.#dropped;
275
+ const failures = this.#uploadFailures;
276
+ const queued = this.record("warn", "runtime-post", "diagnostic_reporter_health", {
277
+ dropped_delta: dropped - this.#reportedDrops,
278
+ upload_failure_delta: failures - this.#reportedFailures,
279
+ pending_event_count: this.pendingCount
280
+ });
281
+ // The health record can itself evict an entry or be refused by a full
282
+ // higher-priority queue. Neither loss has been included in this snapshot.
283
+ if (queued) {
284
+ this.#reportedDrops = dropped;
285
+ this.#reportedFailures = failures;
286
+ }
287
+ }
221
288
  const deadline = Date.now() + timeoutMs;
222
289
  const live = this.#flushLane(this.#live, timeoutMs).then(() => {
223
290
  // Cover records added after drain's last queue check but before its
@@ -262,10 +329,10 @@ export class RuntimeLogReporter {
262
329
  const key = JSON.stringify(first.header);
263
330
  const batch = [];
264
331
  while (batch.length < BATCH_SIZE && lane.queue.length && JSON.stringify(lane.queue[0].header) === key && Boolean(lane.queue[0].crashFile) === Boolean(first.crashFile)) batch.push(lane.queue.shift());
265
- const data = batch.flatMap(({ record }) => [record, {
332
+ const data = batch.flatMap(({ record, metrics, extra }) => [record, {
266
333
  app_version: record.app_version, app_minor_version: "", build_id: "",
267
334
  process_type: record.process_type, session_id: "", timestamp: record.timestamp,
268
- log_type: "flexible", event_name: "codem_runtime_diagnostic", category: record.category, metrics: { count: 1 }
335
+ log_type: "flexible", event_name: "codem_runtime_diagnostic", category: record.category, metrics: metrics ?? { count: 1 }, ...(extra ? { extra } : {})
269
336
  }]);
270
337
  try {
271
338
  await Promise.race([Promise.resolve().then(() => this.#send({ header: first.header, data }, { signal: controller.signal })), deadline]);
@@ -273,6 +340,7 @@ export class RuntimeLogReporter {
273
340
  if (entry.crashFile) { try { fs.unlinkSync(entry.crashFile); } catch { /* Replay may duplicate after a crash or concurrent startup. */ } }
274
341
  }
275
342
  } catch {
343
+ this.#uploadFailures += 1;
276
344
  // One later retry, on the regular interval. Bound both memory and work;
277
345
  // an unavailable collector must not create a retry loop or block exit.
278
346
  for (const entry of batch) {
@@ -303,6 +371,9 @@ export class RuntimeLogReporter {
303
371
 
304
372
  let active;
305
373
 
374
+ export function runtimeLogsEnabled() { return Boolean(active); }
375
+ export function runtimeLogIdentityKey() { return active?.identityKey; }
376
+
306
377
  export function startRuntimeLogs({ role, version, env = process.env, send } = {}) {
307
378
  try {
308
379
  if (active || /^(1|true|yes)$/iu.test(env.CODEM_TELEMETRY_DISABLED ?? "") || env.NODE_TEST_CONTEXT) return;