@altimateai/altimate-code 0.6.0 → 0.7.0

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/CHANGELOG.md CHANGED
@@ -5,6 +5,100 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [0.7.0] - 2026-05-03
9
+
10
+ ### Changed
11
+
12
+ - **Bridged to OpenCode upstream v1.4.0 across a history rewrite.** Upstream rewrote git history between v1.3.17 and v1.4.0 (2026-04-04), leaving zero common ancestor with our fork. A new `script/upstream/bridge-merge.ts` tool overlays v1.4.0's tree file-by-file: take v1.4.0's content for unmarked upstream-shared files, keep main's version entirely for files carrying `altimate_change` markers (61 files), keep `keepOurs` files unchanged (65 files), and drop excluded `skipFiles` (3676 files). PR #18186 (Anthropic provider exclusion handling) is reapplied programmatically. Net diff vs v0.6.1: 361 files, +88,823 / -34,416 LoC. The merge passed nine adversarial audit cycles and ships a 234-test bridge-merge regression suite plus 76-test E2E suite that fails CI loudly on every regression class we hit. (#757)
13
+ - **Pinned the dependency floor for the bridge merge.** Added overrides for `effect@4.0.0-beta.43`, `@effect/platform-node@4.0.0-beta.43`, and `@effect/platform-node-shared@4.0.0-beta.43` (beta.58 removed `ServiceMap` in favor of `Context`). Held back `@ai-sdk/*`, `@openrouter/ai-sdk-provider`, and `@opentui/*` from upstream's bumps. Restored upstream's `solid-js@1.9.10` patchedDependencies declaration (Solid Transition correctness, issue #2046).
14
+
15
+ ### Added
16
+
17
+ - **`--dangerously-skip-permissions` flag on `altimate run`** (upstream PR #21266 backport). Aliased to our existing `--yolo` / `ALTIMATE_CLI_YOLO` branch. Strictly safer than upstream's version: `--yolo` honors explicit `deny` rules in session config, upstream's auto-approves anything not denied with no notion of deny rules. Documented in `docs/docs/usage/cli.md`.
18
+ - **`variant_list` TUI keybind** (upstream PR #21185 backport). Opens the "Switch model variant" dialog (`/variants`); hidden when no variants are configured. Schema entry in `config.ts`; documented in `docs/docs/configure/keybinds.md`.
19
+ - **Plain-text rate-limit retry detection** (upstream PR #21355 backport). `session/retry.ts` now retries Alibaba/DashScope and other providers that return non-JSON 429 responses.
20
+ - **Telemetry diagnostic fields on `agent_outcome`.** `final_tool`, `error_class`, and `reason` are now populated for non-completed outcomes (was empty in ~30% of `abandoned`/`aborted`/`error` runs). `reason` is PII-masked at extraction (capped 500 chars for `error`, 200 for `aborted`); `final_tool` includes MCP-namespaced names (e.g. `mcp__atlassian__getJiraIssue`); `error_class` derives from `classifyError` patterns. Documented in `docs/docs/reference/telemetry.md`.
21
+ - **`bridge-guard` skill** (`.claude/commands/bridge-guard.md`). Codifies the v1.4.0 audit playbook into a reusable runbook covering split-brain module pairs, brand-leak patterns, behavior-patch loss, logo/color regressions, internal-script binary names, and the three-layer safety net. Invoke as `/bridge-guard [PR#]`.
22
+
23
+ ### Fixed
24
+
25
+ - **Permission split-brain — infinite ask loop.** v1.4.0 introduced an Effect-TS `Permission` service in `src/permission/index.ts`, but every runtime ask site still called `PermissionNext.ask` from the older module in `src/permission/next.ts`. The two modules each owned their own pending map: asks landed in `PermissionNext`'s map, the HTTP reply route looked them up in the Effect service's empty map, returned 200 without resolving the deferred. User-visible: clicking "Allow once" / "Allow always" / "Reject" did nothing, the prompt kept re-rendering. Routed both reply routes plus `GET /permission` to `PermissionNext`. Pinned with 4 static-text invariants, 1 tree-wide import scan, and 3 runtime end-to-end deadlock tests.
26
+ - **`mcp add` lost its 7-flag non-interactive mode.** Bridge merge overwrote `McpAddCommand` with upstream's interactive-only version. Scripts/CI calling `mcp add --name foo --type remote --url …` either hung (no TTY) or silently dropped into prompts ignoring args. Restored full `--name --type --url --command --header --oauth --global` flag block with markers.
27
+ - **`mcp remove` command (and `rm` alias) restored** — entirely lost during v1.4.0 merge. `mcp remove <not-found>` now exits non-zero (was exiting 0 because `process.exit(1)` inside the async Effect chain was swallowed).
28
+ - **`SessionStatus.set` / `cancel` async drift.** v1.4.0 made these async but six callers in `prompt.ts` and `processor.ts` were unawaited, racing the idle-state flush on shutdown. All call sites now await; `defer(cancel)` switched to `await using`.
29
+ - **`tool/plan.ts` PlanExitTool reject semantics.** v1.4.0 changed reject from `answer !== "Yes"` to `answer === "No"`, so dialog cancel / dismiss / network drop silently confirmed the agent transition (unsafe). Reverted to "reject on anything but explicit Yes".
30
+ - **SQLite `IMMEDIATE` transaction was silently `DEFERRED`.** `Database.transaction()` wrapper didn't accept the `behavior` option, so `SyncEvent.run`'s read-then-write sequence fell back to deferred locking — concurrent runs could interleave and produce duplicate sequence numbers / corrupt event log. Pass-through added; runtime test exercises 10 parallel writers to prove serialization.
31
+ - **Plugin hook isolation.** `Plugin.trigger()` invoked each hook's callback with no try/catch — a single buggy plugin (third-party or local: codex/copilot/gitlab/etc.) would crash the entire LLM call with a confusing unrelated stack. Wrapped in try/catch with logged failure; iteration continues across remaining hooks.
32
+ - **HTML escaping in OAuth/codex callback templates.** `${error}` was interpolated raw in `mcp/oauth-callback.ts` (real XSS) and `plugin/codex.ts`. Added `escapeHtml()` helper, applied at every interpolation.
33
+ - **Symlink escape in `containsPath` boundary checks.** `project/instance.ts` and `plugin/shared.ts` reverted to `Filesystem.contains()` (lexical) during the bridge merge; restored to `Filesystem.containsReal()` (resolves symlinks before checking project boundary).
34
+ - **Effect Service identifier collision (`@opencode/Account`).** `account/index.ts` and `account/service.ts` both registered the same id with two parallel ManagedRuntimes (undefined merge behaviour). Deleted dead `effect/runtime.ts` and `account/service.ts`; renamed `auth/index.ts` to `@opencode/Auth.cli` to disambiguate from the live `auth/service.ts`.
35
+ - **`chat.params` `maxOutputTokens` hook silently ignored.** `session/llm.ts` computed `maxOutputTokens` *after* the hook fired and never read back from `params.maxOutputTokens` — codex/copilot's own `output.maxOutputTokens = undefined` was a no-op, and any third-party plugin altering this field did nothing. Now matches upstream PRs #21220 + #21225.
36
+ - **`File.search()` race with initial scan.** Cache was empty when tests called search before the initial scan completed. `files()` now tracks the in-flight scan with a `pending` promise and awaits it on call.
37
+ - **`MessageV2.fromError` regressions.** "OAuth token refresh failed" now returns `MessageV2.AuthError` instead of `UnknownError`. `errorMessage()` surfaces stack location for empty-message Error instances. Context-overflow detection added for OpenAI-style `context_length_exceeded` codes.
38
+ - **`workspace-router-middleware.ts` typeerror under `OPENCODE_EXPERIMENTAL_WORKSPACES=1`.** Adaptor API was renamed `fetch → target` in v1.4.0; rewrote to `adaptor.target()` + `ServerProxy.http()` and skip routing for local targets.
39
+ - **`Truncate.init()` lost from `project/bootstrap.ts`.** Hourly scheduler that prunes `Global.Path.data/tool-output/tool_*` never re-registered, so the directory grew unboundedly. Restored.
40
+ - **`provider/models.ts` `setTimeout(…, 0)` deferral on initial refresh.** Removed during merge, re-introducing the circular-dep risk fixed by altimate `980efaab64`. Restored.
41
+ - **TUI mid-render display corruption.** `clipboard.ts`, `dialog-workspace-list.tsx`, `dialog-mcp.tsx` had `console.log` writing directly to the terminal mid-render. Restored structured `Log.Default.debug` calls and removed a stray workspace-result `JSON.stringify` debug-print.
42
+ - **Workspace SSE reconnect loop killed by transient network blips.** `control-plane/workspace.ts` lost its defensive `.catch(() => undefined)` and a `return` from the local-workspace branch was permanent rather than per-iteration. Restored both.
43
+ - **Branding restoration after v1.4.0 leaks.** Fixed in three batches: cycle-7 audit (196 leaks across 66 files: `$schema` URLs, system prompts, repo references, generated SDK strings, route descriptions); ASCII logo (`ALTIMATE | CODE` block letters were swapped for `OPEN | CODE`); brand colors on the startup logo (`theme.primary` warm orange + `theme.accent` purple were replaced with monochrome grey/white); `attach.ts` Basic-auth username (was `opencode:`, broke authentication against altimate-code servers); `cli/cmd/github.ts` AGENT_USERNAME / WORKFLOW_FILE / GitHub App slug / OIDC audience / branch prefix; CLI describe text in 8+ subcommands; `mcp` resolveConfigPath order (`altimate-code.json` precedence with `opencode.json` fallback for existing installs); plugin install path (`.altimate-code/` for new installs, keep `.opencode/` if it exists).
44
+ - **API-key leakage in masked strings.** `maskString` only redacted quoted spans. Added unquoted-API-key (`sk-`, `sk-ant-`, `sk-proj-`) and `Bearer …` patterns with a 20-char length floor (avoids false positives on short identifiers like `sk-foo`). All 9 existing `maskString` consumers pick this up.
45
+ - **`ERROR_PATTERNS` coverage for HTTP 5xx + rate-limit prose.** Real provider errors like `APIError: Service unavailable (503)` and `Rate limit exceeded. Retry after 60s` were classifying as `unknown` because patterns only matched the prefixed forms. Added phrases: `service unavailable`, `rate limit`, `rate_limit`, `retry after`, `too many requests`, `503`, `502`, `504` to the `http_error` class.
46
+ - **`marker-guard` strict mode on bridge-merge pushes to main.** PR-side guard already runs non-strict for `upstream/merge-*` head_refs, but the squash-merge push event has no head_ref — was failing on the hundreds of upstream files the bridge brings in. Now detects bridge/upstream-merge commits in the pushed range by subject (`grep -qiE '(bridge|merge) upstream'`) and downgrades strict→non-strict. (#782, closes #781)
47
+
48
+ ### Security
49
+
50
+ - **API-key redaction at the diagnostic surface.** `Telemetry.deriveAgentOutcomeReason()` applies `maskString` to both `error` and `aborted` reasons before they enter the `agent_outcome.reason` field (which surfaces in our backend telemetry). `prompt.ts` extracts only `err.data.message` instead of `JSON.stringify(error.data)` (which leaked `responseBody` / `responseHeaders` / metadata).
51
+ - **Plugin hook crash containment.** A single plugin throwing in `chat.params` / `chat.headers` / any registered hook no longer takes down the entire LLM call (see Fixed).
52
+
53
+ ### Internal
54
+
55
+ - **Three-layer regression backstop for bridge merges.** (1) `script/upstream/analyze.ts --branding` now catches bare `opencode` strings in user-visible contexts (yargs `describe`, console output, MCP `clientInfo.name`, User-Agent, OIDC audiences, GitHub workflow YAML, `spawn` binary names); skips lines inside `altimate_change` blocks. (2) New `MergeConfig.requireMarkers` allowlist of 38 files known to hold altimate behavioral patches; `bridge-merge.ts` treats them as `keepOurs` and hard-aborts the merge if any is missing markers. (3) Retroactive marker sweep added markers to 14 previously-unmarked files. CI runs `--branding` and `--require-markers --strict` on every PR.
56
+ - **9 audit cycles documented and pinned.** 310 tests across 8 files in `test/upstream/` (`bridge-merge`, `bridge-merge-invariants`, `bridge-merge-runtime`, `bridge-merge-v3`, `bridge-merge-e2e`, `v140-merge-adversarial`, `v140-merge-fuzz`, `v140-merge-chaos`, `v140-permission-deadlock`).
57
+ - **`tracing.ts` `flushSync` vs async `snapshot` race.** Crashed-trace file could be clobbered by an in-flight async snapshot completing its `fs.rename` after `flushSync`'s sync write. Added a `crashed` flag with two checkpoint reads on the async path. Plus `Recap.flush()` helper to replace `setTimeout(50)` waits in tests on slow CI runners.
58
+ - **GitGuardian whitelisting.** `.gitguardian.yaml` paths-ignore for the `protected.ts` deny-list (filenames like `.pgpass` / `id_rsa` / `credentials.json` are filenames, not credentials); split filename literals via `DOT + "name"` concatenation so the GG dashboard doesn't keep flagging them; replaced JWT-shaped synthetic tokens in the v140 adversarial suite with generic 36-char alphanumeric tokens.
59
+ - **`.github/meta/{commit,diff,pr-body-*}` untracked + gitignored.** Per CLAUDE.md these are transient scratch files; tracked instances were leaking false-positive branding hits and stale commit messages. Pattern broadened to `**/.github/meta/...` so nested paths are covered.
60
+ - **`globalThis.fetch` leak in `session-proxy-middleware.test.ts`.** Test stubbed `fetch` but never restored it, cascading into ~15 unrelated test failures (OAuth, ECONNRESET, HttpExporter, Discovery, live-trace-viewer). Now snapshots and restores in `afterEach`.
61
+ - **Restored `.github/TEAM_MEMBERS`.** Bridge merge replaced our 30-name list with upstream's 15-name list, removing 28 altimate maintainers including `anandgupta42` — `pr-standards.yml` was flagging every team member's PR with `needs:title` / `needs:compliance` and auto-closing after 2 hours.
62
+
63
+ ### Testing
64
+
65
+ - **200+ new tests pinning every audit-cycle fix:** static-text invariants for service identifiers, async signatures, marker integrity, branding; runtime tests for `Database.transaction` IMMEDIATE serialization, `BusEvent.define` idempotency, `SyncEvent` ⊆ `BusEvent` registry bridge, `PlanExitTool` reject semantics, plugin trigger isolation; property-based fuzz of `maskString` (5×500 random iterations on unicode/control-chars/emoji), `deriveAgentOutcomeReason` field bounds; chaos: 100 parallel `deriveAgentOutcomeReason` calls, regex DoS resistance with relative-budget thresholds; E2E: spawns the CLI as a real user (`bun run src/index.ts <args>` with isolated `XDG_*` env vars) and visually inspects `--version`, `--help`, `mcp --help`, theme schemas, system-prompt files, etc.
66
+ - **Hardened flaky tests against parallel-suite contention.** Extracted pure helpers from `tui/thread.ts` to remove `mock.module()` calls that leaked across files. Replaced FIFO request-matching queue in `session/llm.test.ts` with a path-keyed Map (out-of-order requests no longer resolve another test's deferred). Added explicit `30_000ms` timeouts to slow-SDK tests. Local full suite: 7965 pass / 0 fail.
67
+
68
+ ### Documentation
69
+
70
+ - **README refreshed for v0.5.12 through v0.6.1** — adds three Key Features entries (cross-dialect data parity, automated dbt unit tests, GitLab MR review), four providers (Altimate LLM Gateway, Databricks AI Gateway, Snowflake Cortex, LM Studio), Microsoft Fabric warehouse footnote, `/data-parity` and `/dbt-unit-tests` quick-demo examples, inline list of the 19 built-in skills. Source of truth is CHANGELOG.md; bullets are condensed restatements. (#784)
71
+
72
+ ## [0.6.1] - 2026-04-24
73
+
74
+ ### Fixed
75
+
76
+ - **BigQuery finops tools were broken — now work, in any region.** `finops_query_history` was failing 100% on BigQuery with `Unrecognized name: error_message at [11:5]`. Three separate bugs in the `INFORMATION_SCHEMA.JOBS` template: (a) `error_message` and `error_code` were read as top-level columns but they only exist inside the `error_result` struct — now reads `error_result.message` and `error_result.reason`; (b) `total_rows` is a `PARTITIONS` column, not `JOBS` — replaced with `CAST(NULL AS INT64) AS rows_produced`; (c) BigQuery's `state` returns `'DONE'`, not `'SUCCESS'`, so the summary loop was reporting every completed job as FAILED — now derives `execution_status` from `error_result IS NULL`. Every successful BQ job now reports as SUCCESS. (#739, closes #738)
77
+ - **BigQuery finops unusable outside US region.** All five finops modules (`finops_query_history`, `finops_analyze_credits`, `finops_expensive_queries`, `finops_warehouse_advice`, `finops_unused_resources`, `finops_role_grants`) hardcoded `` `region-US.INFORMATION_SCHEMA.*` ``. Now reads the BigQuery connection's configured `location` (e.g. `us`, `eu`, `us-central1`, `asia-northeast1`), sanitised via an `[a-z0-9-]` allowlist with a 64-char cap and hyphen trim. If `location` is unset the tool defaults to `us` — **set `location` explicitly on the BigQuery connection for non-US projects** or you will query the wrong region. Snowflake and Databricks paths are unchanged. (#739)
78
+
79
+ ### Changed
80
+
81
+ - **`@altimateai/altimate-core` 0.3.0 → 0.3.1** — upstream patch release of the native SQL type-checker used by `altimate_core_validate` and the cross-dialect data-parity engine.
82
+
83
+ ### Docs
84
+
85
+ - **New showcase page** (`docs/examples/`) with 12 end-to-end workflow demos — dbt peer review, column-level lineage diff, MS SQL → Fabric migration, Fabric platform admin, upstream schema change, NYC Taxi, Olist, Spotify, and more. (#742)
86
+ - **`location` field documented** on the BigQuery connection and called out on the finops tools page.
87
+
88
+ ### Data handling
89
+
90
+ - `finops_query_history`, `finops_analyze_credits`, and `finops_warehouse_advice` read `user_email` (BigQuery), `user_name` (Snowflake / Databricks / ClickHouse), and raw `query_text` from warehouse system views. Results are returned to the agent and enter the LLM context window. No telemetry or backend upload. Review your tenant's data-handling policy before enabling finops tools in regulated (PII / PHI / PCI) environments.
91
+ - `finops_unused_resources` on BigQuery reads `INFORMATION_SCHEMA.TABLE_STORAGE`, which is an org-level view. Project-scoped service accounts typically require `bigquery.resourceAdmin` at the org to avoid a permission error.
92
+
93
+ ### Internal
94
+
95
+ - `anti-slop` CI workflow is now advisory — labels + comments still fire on blocked-term hits, but the workflow no longer auto-closes PRs. Root cause: the repo's pull-request template embeds an HTML comment instructing AI to insert "PINEAPPLE", which `anti-slop.yml` also blocklists — every AI-assisted team-member PR was auto-closing within two minutes. (#741, closes #740)
96
+ - Marker-guard hotfix: the `isValidDatabricksHost` env-fallback path added in v0.6.0 straddled the post-push Marker Guard's `-U5` diff window; wrapped in an inline `altimate_change` marker pair to keep strict mode green.
97
+
98
+ ### Testing
99
+
100
+ - New adversarial tests for v0.6.1 in `packages/opencode/test/skill/release-v0.6.1-adversarial.test.ts`: `sanitizeBqRegion` injection vectors (CRLF, null bytes, backticks, path traversal, Unicode homoglyphs, prototype-pollution-adjacent inputs), `interpolateBqRegion` idempotency and multi-placeholder safety, `bqRegionFor` registry edge cases, BIGQUERY_HISTORY_SQL column-name regression guards (all four #739 bugs), cross-module "every BQ template uses `{region}`" guard, and full-pipeline `buildHistoryQuery` behaviour including Snowflake / Databricks no-regression guards.
101
+
8
102
  ## [0.6.0] - 2026-04-21
9
103
 
10
104
  ### Added
package/LICENSE CHANGED
@@ -1,6 +1,5 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2026 Altimate AI
4
3
  Copyright (c) 2025 opencode
5
4
 
6
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
@@ -24,7 +24,7 @@ from typing import (
24
24
 
25
25
  import agate
26
26
  import json
27
- from dbt.adapters.factory import get_adapter, register_adapter
27
+ from dbt.adapters.factory import FACTORY, get_adapter, register_adapter
28
28
  from dbt.config.runtime import RuntimeConfig
29
29
  from dbt.flags import set_from_args
30
30
  from dbt.parser.manifest import ManifestLoader, process_node
@@ -319,6 +319,22 @@ class DbtProject:
319
319
  self.config = RuntimeConfig.from_args(self.args)
320
320
  if hasattr(self.config, "source_paths"):
321
321
  self.config.model_paths = self.config.source_paths
322
+ # dbt's adapter factory silently short-circuits register_adapter() if
323
+ # an adapter of the same type is already registered, so a target
324
+ # switch that keeps the adapter type (e.g. two Databricks targets or
325
+ # two DuckDB targets with different credentials) would otherwise
326
+ # leave us with a stale adapter still bound to the previous target's
327
+ # host / http_path / path. Evict it here before re-registering so
328
+ # the new config's credentials actually take effect. See
329
+ # AltimateAI/vscode-dbt-power-user#1676 and #1737.
330
+ adapter_name = self.config.credentials.type
331
+ if adapter_name in FACTORY.adapters:
332
+ try:
333
+ FACTORY.adapters[adapter_name].cleanup_connections()
334
+ except Exception:
335
+ pass
336
+ with FACTORY.lock:
337
+ FACTORY.adapters.pop(adapter_name, None)
322
338
  if DBT_MAJOR_VER >= 1 and DBT_MINOR_VER >= 8:
323
339
  from dbt.mp_context import get_mp_context
324
340
  register_adapter(self.config, get_mp_context())
@@ -16,6 +16,9 @@ try:
16
16
  except ImportError:
17
17
  HAS_AGATE = False
18
18
 
19
+ # JavaScript Number.MAX_SAFE_INTEGER — beyond this, floats lose precision
20
+ MAX_SAFE_INTEGER = 9007199254740991
21
+
19
22
 
20
23
  def to_dict(obj, visited=None):
21
24
  if visited is None:
@@ -28,16 +31,40 @@ def to_dict(obj, visited=None):
28
31
 
29
32
  if HAS_AGATE and isinstance(obj, agate.Table):
30
33
  visited.add(obj_id)
34
+ rows = [to_dict(row, visited) for row in obj.rows]
35
+ # Detect columns with large integers that were converted to strings
36
+ column_types = [x.__class__.__name__ for x in obj.column_types]
37
+ for col_idx, col_type in enumerate(column_types):
38
+ if col_type in ("Number", "Integer") and any(
39
+ isinstance(row[col_idx], str) for row in rows if row[col_idx] is not None
40
+ ):
41
+ column_types[col_idx] = "BigInteger"
31
42
  result = {
32
- "rows": [to_dict(row, visited) for row in obj.rows],
43
+ "rows": rows,
33
44
  "column_names": obj.column_names,
34
- "column_types": list(map(lambda x: x.__class__.__name__, obj.column_types)),
45
+ "column_types": column_types,
35
46
  }
36
47
  visited.remove(obj_id)
37
48
  return result
38
49
  if isinstance(obj, str):
39
50
  return obj
51
+ # Encode binary payloads as lowercase hex strings BEFORE falling into the
52
+ # Iterable branch below. Otherwise ``bytes`` gets expanded into a
53
+ # ``list[int]`` (one int per byte), which for real binary columns blows
54
+ # past python-bridge's IPC pipe buffer and surfaces as
55
+ # ``Error [ERR_IPC_DISCONNECTED]`` on the extension side. See
56
+ # AltimateAI/vscode-dbt-power-user#1747. ``memoryview`` is normalized
57
+ # through ``bytes()`` first because some adapters hand back views over
58
+ # a pyarrow buffer rather than a raw ``bytes`` object.
59
+ if isinstance(obj, (bytes, bytearray)):
60
+ return obj.hex()
61
+ if isinstance(obj, memoryview):
62
+ return bytes(obj).hex()
63
+ if isinstance(obj, int) and abs(obj) > MAX_SAFE_INTEGER:
64
+ return str(obj)
40
65
  if isinstance(obj, Decimal):
66
+ if obj == obj.to_integral_value() and abs(obj) > MAX_SAFE_INTEGER:
67
+ return str(int(obj))
41
68
  return float(obj)
42
69
  if isinstance(obj, (datetime, date, time)):
43
70
  return obj.isoformat()