@doist/doistbot-cli 1.0.5 → 1.0.6

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.
Files changed (53) hide show
  1. package/dist/actions/auth.js +55 -21
  2. package/dist/actions/doctor.js +13 -6
  3. package/dist/actions/review.js +75 -10
  4. package/dist/auth.js +51 -6
  5. package/dist/config.js +34 -5
  6. package/dist/env.js +1 -0
  7. package/dist/runtime.js +1 -1
  8. package/dist/terminal.js +2 -0
  9. package/package.json +4 -4
  10. package/sandbox/_node_modules/doistbot-repo-config/dist/index.d.ts +4 -8
  11. package/sandbox/_node_modules/doistbot-repo-config/dist/index.js +8 -20
  12. package/sandbox/dist/core/check-run.js +16 -0
  13. package/sandbox/dist/core/datadog-metrics.js +6 -52
  14. package/sandbox/dist/core/logger-span-processor.js +78 -0
  15. package/sandbox/dist/core/pi.js +257 -176
  16. package/sandbox/dist/core/tracing-setup.js +15 -0
  17. package/sandbox/dist/core/tracing.js +71 -0
  18. package/sandbox/dist/main.js +4 -1
  19. package/sandbox/dist/providers/credentials.js +12 -2
  20. package/sandbox/dist/providers/gemini.js +5 -1
  21. package/sandbox/dist/providers/helpers.js +12 -17
  22. package/sandbox/dist/providers/index.js +4 -0
  23. package/sandbox/dist/providers/openrouter-pi.js +25 -0
  24. package/sandbox/dist/providers/pi-review.js +12 -1
  25. package/sandbox/dist/tasks/chat/chat.js +161 -101
  26. package/sandbox/dist/tasks/issue-fix-retry/fix-retry.js +210 -185
  27. package/sandbox/dist/tasks/issue-summarize/model.js +1 -1
  28. package/sandbox/dist/tasks/issue-triage/clone.js +40 -0
  29. package/sandbox/dist/tasks/issue-triage/context.js +25 -1
  30. package/sandbox/dist/tasks/issue-triage/fix-attempt.js +31 -8
  31. package/sandbox/dist/tasks/issue-triage/fix-dispatch.js +154 -101
  32. package/sandbox/dist/tasks/issue-triage/fix-loop/runners.js +15 -6
  33. package/sandbox/dist/tasks/issue-triage/model.js +120 -36
  34. package/sandbox/dist/tasks/issue-triage/output.js +27 -0
  35. package/sandbox/dist/tasks/issue-triage/pr-creator.js +3 -2
  36. package/sandbox/dist/tasks/issue-triage/resolution.js +26 -0
  37. package/sandbox/dist/tasks/issue-triage/routing.js +9 -9
  38. package/sandbox/dist/tasks/issue-triage/side-effects.js +9 -0
  39. package/sandbox/dist/tasks/issue-triage/triage.js +288 -418
  40. package/sandbox/dist/tasks/review/check-run.js +2 -1
  41. package/sandbox/dist/tasks/review/conversation-context.js +2 -1
  42. package/sandbox/dist/tasks/review/engines/dedupe.js +5 -4
  43. package/sandbox/dist/tasks/review/engines/multi-focus.js +40 -9
  44. package/sandbox/dist/tasks/review/engines/shared.js +8 -6
  45. package/sandbox/dist/tasks/review/github-comment-format.js +64 -10
  46. package/sandbox/dist/tasks/review/local.js +2 -4
  47. package/sandbox/dist/tasks/review/review-summary.js +45 -7
  48. package/sandbox/dist/tasks/review/review.js +38 -24
  49. package/sandbox/dist/tasks/review/runner.js +23 -2
  50. package/sandbox/dist/tasks/review/summary-model.js +164 -17
  51. package/sandbox/dist/tasks/review/thinking-level.js +4 -0
  52. package/sandbox/node_modules/doistbot-repo-config/dist/index.d.ts +4 -8
  53. package/sandbox/node_modules/doistbot-repo-config/dist/index.js +8 -20
@@ -1,13 +1,18 @@
1
+ import { createDatadogMetrics, resolveDatadogApiKey, resolveStandardObservabilityTags, } from 'doistbot-datadog-metrics';
1
2
  import { logger } from './logging.js';
2
3
  const TASK_COMPLETION_METRIC = 'doistbot.task.completion';
3
4
  const TASK_DURATION_METRIC = 'doistbot.task.duration_seconds';
4
- const DATADOG_REQUEST_TIMEOUT_MS = 5_000;
5
5
  let hasWarnedAboutMissingDatadogApiKey = false;
6
+ const { postDatadogMetric } = createDatadogMetrics({ logger });
6
7
  function toTaskMetricBaseModel(model) {
7
8
  switch (model) {
9
+ case 'deepseek':
10
+ return 'deepseek';
8
11
  case 'gemini':
9
12
  case 'google':
10
13
  return 'google';
14
+ case 'zai':
15
+ return 'zai';
11
16
  case 'openai':
12
17
  return 'openai';
13
18
  case 'none':
@@ -39,57 +44,6 @@ function buildTaskMetricTags({ type, repository, model, outcome, reason, }) {
39
44
  }
40
45
  return tags;
41
46
  }
42
- // Doist Observability Standard requires service/env/version on metrics submitted
43
- // directly to the API — the host agent would otherwise inject them.
44
- function resolveStandardObservabilityTags() {
45
- const tags = [];
46
- const service = process.env.DD_SERVICE?.trim();
47
- const env = process.env.DD_ENV?.trim();
48
- const version = process.env.DD_VERSION?.trim();
49
- if (service)
50
- tags.push(`service:${service}`);
51
- if (env)
52
- tags.push(`env:${env}`);
53
- if (version)
54
- tags.push(`version:${version}`);
55
- return tags;
56
- }
57
- function resolveDatadogApiKey() {
58
- const apiKey = process.env.DATADOG_API_KEY?.trim();
59
- return apiKey || null;
60
- }
61
- function resolveDatadogSite() {
62
- return process.env.DATADOG_SITE?.trim() || process.env.DD_SITE?.trim() || 'datadoghq.com';
63
- }
64
- async function postDatadogMetric({ apiKey, path, body, }) {
65
- const url = `https://api.${resolveDatadogSite()}${path}`;
66
- try {
67
- const response = await fetch(url, {
68
- method: 'POST',
69
- signal: AbortSignal.timeout(DATADOG_REQUEST_TIMEOUT_MS),
70
- headers: {
71
- 'Content-Type': 'application/json',
72
- 'DD-API-KEY': apiKey,
73
- },
74
- body: JSON.stringify(body),
75
- });
76
- if (response.ok) {
77
- return;
78
- }
79
- const errorBody = (await response.text()).slice(0, 500);
80
- logger.warn('Failed to submit Datadog task metric', {
81
- path,
82
- status: response.status,
83
- errorBody,
84
- });
85
- }
86
- catch (error) {
87
- logger.warn('Failed to submit Datadog task metric', {
88
- path,
89
- error: error instanceof Error ? error.message : String(error),
90
- });
91
- }
92
- }
93
47
  export async function recordTaskMetrics({ type, repository, model, outcome, durationMs, reason, }) {
94
48
  const apiKey = resolveDatadogApiKey();
95
49
  if (!apiKey) {
@@ -0,0 +1,78 @@
1
+ import { SpanStatusCode, trace } from '@opentelemetry/api';
2
+ import { logger } from './logging.js';
3
+ const MAX_ATTR_SERIALIZED_LEN = 4096;
4
+ export function hrTimeToMs(hrTime) {
5
+ const [seconds, nanos] = hrTime;
6
+ return Math.round(seconds * 1000 + nanos / 1_000_000);
7
+ }
8
+ function capString(value) {
9
+ if (value.length <= MAX_ATTR_SERIALIZED_LEN)
10
+ return value;
11
+ // Reserve room for the marker so the FINAL emitted value stays <= the cap.
12
+ const marker = `…[truncated, ${value.length} chars]`;
13
+ return `${value.slice(0, Math.max(0, MAX_ATTR_SERIALIZED_LEN - marker.length))}${marker}`;
14
+ }
15
+ export function truncateAttrs(attrs) {
16
+ const out = {};
17
+ for (const [key, value] of Object.entries(attrs)) {
18
+ if (key === 'doistbot.kind')
19
+ continue; // internal marker, never emitted
20
+ if (typeof value === 'string') {
21
+ out[key] = capString(value);
22
+ }
23
+ else if (value !== null && typeof value === 'object') {
24
+ // Arrays/objects (e.g. decision paths, parsed-label arrays): keep
25
+ // the native value if its JSON serialization is under the cap;
26
+ // otherwise replace it with the truncated serialized string.
27
+ const json = JSON.stringify(value);
28
+ out[key] = json.length > MAX_ATTR_SERIALIZED_LEN ? capString(json) : value;
29
+ }
30
+ else {
31
+ out[key] = value; // number / boolean / null — inherently small
32
+ }
33
+ }
34
+ return out;
35
+ }
36
+ // Parent span id access on a finished span is SDK-version-specific. Prefer the
37
+ // newer `parentSpanContext`, fall back to the older `parentSpanId`. Only used
38
+ // in onEnd — onStart derives the parent from the parent Context instead.
39
+ function parentSpanIdOf(span) {
40
+ const withParentCtx = span;
41
+ if (withParentCtx.parentSpanContext?.spanId)
42
+ return withParentCtx.parentSpanContext.spanId;
43
+ const withParentId = span;
44
+ return withParentId.parentSpanId ?? null;
45
+ }
46
+ // Span attributes are spread FIRST so task attributes can never overwrite the
47
+ // envelope fields (event/name/span_id/parent_span_id/duration_ms/status/error)
48
+ // that the Datadog pipeline and the visualizer key on. Timestamps come from
49
+ // the logging system, never from meta.
50
+ export class LoggerSpanProcessor {
51
+ onStart(span, parentContext) {
52
+ const readable = span;
53
+ const isTool = readable.attributes['doistbot.kind'] === 'tool';
54
+ logger.info(`${isTool ? 'tool' : 'phase'}.start: ${readable.name}`, {
55
+ ...truncateAttrs(readable.attributes),
56
+ event: isTool ? 'tool.start' : 'phase.start',
57
+ name: readable.name,
58
+ span_id: readable.spanContext().spanId,
59
+ parent_span_id: trace.getSpanContext(parentContext)?.spanId ?? null,
60
+ });
61
+ }
62
+ onEnd(span) {
63
+ const isTool = span.attributes['doistbot.kind'] === 'tool';
64
+ const status = span.status.code === SpanStatusCode.ERROR ? 'error' : 'ok';
65
+ logger.info(`${isTool ? 'tool' : 'phase'}.end: ${span.name}`, {
66
+ ...truncateAttrs(span.attributes),
67
+ event: isTool ? 'tool.end' : 'phase.end',
68
+ name: span.name,
69
+ span_id: span.spanContext().spanId,
70
+ parent_span_id: parentSpanIdOf(span),
71
+ duration_ms: hrTimeToMs(span.duration),
72
+ status,
73
+ ...(status === 'error' && span.status.message ? { error: span.status.message } : {}),
74
+ });
75
+ }
76
+ async forceFlush() { }
77
+ async shutdown() { }
78
+ }