agent-first-data 0.2.3__tar.gz → 0.3.0__tar.gz

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.
@@ -1,7 +1,7 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-first-data
3
- Version: 0.2.3
4
- Summary: Agent-First Data (AFD) — suffix-driven output formatting and protocol templates for AI agents
3
+ Version: 0.3.0
4
+ Summary: Agent-First Data (AFDATA) — suffix-driven output formatting and protocol templates for AI agents
5
5
  License-Expression: MIT
6
6
  Project-URL: Repository, https://github.com/cmnspore/agent-first-data
7
7
  Requires-Python: >=3.9
@@ -9,7 +9,7 @@ Description-Content-Type: text/markdown
9
9
 
10
10
  # agent-first-data
11
11
 
12
- **Agent-First Data (AFD)** — Suffix-driven output formatting and protocol templates for AI agents.
12
+ **Agent-First Data (AFDATA)** — Suffix-driven output formatting and protocol templates for AI agents.
13
13
 
14
14
  The field name is the schema. Agents read `latency_ms` and know milliseconds, `api_key_secret` and know to redact, no external schema needed.
15
15
 
@@ -27,24 +27,39 @@ A backup tool invoked from the CLI — flags, env vars, and config all use the s
27
27
  API_KEY_SECRET=sk-1234 cloudback --timeout-s 30 --max-file-size-bytes 10737418240 /data/backup.tar.gz
28
28
  ```
29
29
 
30
- The tool reads env vars, flags, and config — all with AFD suffixes — and emits a startup message:
30
+ For CLI diagnostics, enable log categories explicitly:
31
+
32
+ ```bash
33
+ --log startup,request,progress,retry,redirect
34
+ --verbose # shorthand for all categories
35
+ ```
36
+
37
+ Without these flags, startup diagnostics should stay off by default.
38
+
39
+ The tool reads env vars, flags, and config — all with AFDATA suffixes — and can emit a startup diagnostic event:
31
40
 
32
41
  ```python
33
42
  from agent_first_data import *
34
43
  import os
35
44
 
36
- startup = build_json_startup(
37
- {"timeout_s": 30, "max_file_size_bytes": 10737418240},
38
- {"input_path": "/data/backup.tar.gz"},
39
- {"API_KEY_SECRET": os.environ.get("API_KEY_SECRET")},
45
+ startup = build_json(
46
+ "log",
47
+ {
48
+ "event": "startup",
49
+ "config": {"timeout_s": 30, "max_file_size_bytes": 10737418240},
50
+ "args": {"input_path": "/data/backup.tar.gz"},
51
+ "env": {"API_KEY_SECRET": os.environ.get("API_KEY_SECRET")},
52
+ },
53
+ trace=None,
40
54
  )
41
55
  ```
42
56
 
43
57
  Three output formats, same data:
44
58
 
45
59
  ```
46
- JSON: {"code":"startup","args":{"input_path":"/data/backup.tar.gz"},"config":{"max_file_size_bytes":10737418240,"timeout_s":30},"env":{"API_KEY_SECRET":"***"}}
47
- YAML: code: "startup"
60
+ JSON: {"code":"log","event":"startup","args":{"input_path":"/data/backup.tar.gz"},"config":{"max_file_size_bytes":10737418240,"timeout_s":30},"env":{"API_KEY_SECRET":"***"}}
61
+ YAML: code: "log"
62
+ event: "startup"
48
63
  args:
49
64
  input_path: "/data/backup.tar.gz"
50
65
  config:
@@ -52,23 +67,20 @@ YAML: code: "startup"
52
67
  timeout: "30s"
53
68
  env:
54
69
  API_KEY: "***"
55
- Plain: args.input_path=/data/backup.tar.gz code=startup config.max_file_size=10.0GB config.timeout=30s env.API_KEY=***
70
+ Plain: args.input_path=/data/backup.tar.gz code=log event=startup config.max_file_size=10.0GB config.timeout=30s env.API_KEY=***
56
71
  ```
57
72
 
58
73
  `--timeout-s` → `timeout_s` → `timeout: 30s`. `API_KEY_SECRET` → `API_KEY: "***"`. The suffix is the schema.
59
74
 
60
75
  ## API Reference
61
76
 
62
- Total: **9 public APIs** + **AFD logging** (4 protocol builders + 3 output functions + 1 internal + 1 utility)
77
+ Total: **8 public APIs** + **AFDATA logging** (3 protocol builders + 3 output functions + 1 internal + 1 utility)
63
78
 
64
79
  ### Protocol Builders (returns dict)
65
80
 
66
- Build AFD protocol structures. Return dict objects for API responses.
81
+ Build AFDATA protocol structures. Return dict objects for API responses.
67
82
 
68
83
  ```python
69
- # Startup (configuration)
70
- build_json_startup(config: Any, args: Any, env: Any) -> dict
71
-
72
84
  # Success (result)
73
85
  build_json_ok(result: Any, trace: Any = None) -> dict
74
86
 
@@ -86,10 +98,15 @@ build_json(code: str, fields: Any, trace: Any = None) -> dict
86
98
  from agent_first_data import *
87
99
 
88
100
  # Startup
89
- startup = build_json_startup(
90
- {"api_key_secret": "sk-123", "timeout_s": 30},
91
- {"config_path": "config.yml"},
92
- {"RUST_LOG": "info"},
101
+ startup = build_json(
102
+ "log",
103
+ {
104
+ "event": "startup",
105
+ "config": {"api_key_secret": "sk-123", "timeout_s": 30},
106
+ "args": {"config_path": "config.yml"},
107
+ "env": {"RUST_LOG": "info"},
108
+ },
109
+ trace=None,
93
110
  )
94
111
 
95
112
  # Success (always include trace)
@@ -196,14 +213,20 @@ async def get_user(user_id: int):
196
213
  from agent_first_data import *
197
214
 
198
215
  # 1. Startup
199
- startup = build_json_startup(
200
- {"api_key_secret": "sk-sensitive-key", "timeout_s": 30},
201
- {"input_path": "data.json"},
202
- {"RUST_LOG": "info"},
216
+ startup = build_json(
217
+ "log",
218
+ {
219
+ "event": "startup",
220
+ "config": {"api_key_secret": "sk-sensitive-key", "timeout_s": 30},
221
+ "args": {"input_path": "data.json"},
222
+ "env": {"RUST_LOG": "info"},
223
+ },
224
+ trace=None,
203
225
  )
204
226
  print(output_yaml(startup))
205
227
  # ---
206
- # code: "startup"
228
+ # code: "log"
229
+ # event: "startup"
207
230
  # args:
208
231
  # input_path: "data.json"
209
232
  # config:
@@ -294,23 +317,23 @@ print(output_plain(data))
294
317
  # api_key=*** cache_ttl=3600s count=42 created_at=2025-02-07T00:00:00.000Z file_size=5.0MB payment=50000000msats price=$99.99 request_timeout=5.0s success_rate=95.5% user_name=alice
295
318
  ```
296
319
 
297
- ## AFD Logging
320
+ ## AFDATA Logging
298
321
 
299
- AFD-compliant structured logging via Python's `logging` module. Every log line is formatted using the library's own `output_json`/`output_plain`/`output_yaml` functions. Span fields are carried via `contextvars` (async-safe), automatically flattened into each log line.
322
+ AFDATA-compliant structured logging via Python's `logging` module. Every log line is formatted using the library's own `output_json`/`output_plain`/`output_yaml` functions. Span fields are carried via `contextvars` (async-safe), automatically flattened into each log line.
300
323
 
301
324
  ### API
302
325
 
303
326
  ```python
304
327
  from agent_first_data import init_logging_json, init_logging_plain, init_logging_yaml
305
- from agent_first_data.afd_logging import AfdHandler, get_logger, span
328
+ from agent_first_data.afdata_logging import AfdataHandler, get_logger, span
306
329
 
307
- # Convenience initializers — set up the root logger with AFD output to stdout
330
+ # Convenience initializers — set up the root logger with AFDATA output to stdout
308
331
  init_logging_json(level="INFO") # Single-line JSONL (secrets redacted, original keys)
309
332
  init_logging_plain(level="INFO") # Single-line logfmt (keys stripped, values formatted)
310
333
  init_logging_yaml(level="INFO") # Multi-line YAML (keys stripped, values formatted)
311
334
 
312
335
  # Low-level — create a handler for custom logger stacks
313
- AfdHandler(format="json") # format: "json" | "plain" | "yaml"
336
+ AfdataHandler(format="json") # format: "json" | "plain" | "yaml"
314
337
 
315
338
  # Logger with default fields (returns logging.LoggerAdapter)
316
339
  get_logger(name, **fields)
@@ -391,8 +414,8 @@ The `code` field defaults to the log level. Override with an explicit field:
391
414
  from agent_first_data import get_logger
392
415
 
393
416
  logger = get_logger("myapp")
394
- logger.info("Server ready", extra={"code": "startup"})
395
- # {"timestamp_epoch_ms":...,"message":"Server ready","target":"myapp","code":"startup"}
417
+ logger.info("Server ready", extra={"code": "log", "event": "startup"})
418
+ # {"timestamp_epoch_ms":...,"message":"Server ready","target":"myapp","code":"log","event":"startup"}
396
419
  ```
397
420
 
398
421
  ### Output Fields
@@ -410,7 +433,7 @@ Every log line contains:
410
433
 
411
434
  ### Log Output Formats
412
435
 
413
- All three formats use the library's own output functions, so AFD suffix processing applies to log fields too:
436
+ All three formats use the library's own output functions, so AFDATA suffix processing applies to log fields too:
414
437
 
415
438
  | Format | Function | Keys | Values | Use case |
416
439
  |:-------|:---------|:-----|:-------|:---------|
@@ -440,6 +463,21 @@ All formats automatically redact `_secret` fields.
440
463
  - **Currency**: `_msats`, `_sats`, `_btc`, `_usd_cents`, `_eur_cents`, `_jpy`, `_{code}_cents`
441
464
  - **Other**: `_percent`, `_secret` (auto-redacted in all formats)
442
465
 
466
+ ## Repository
467
+
468
+ This package is part of the [agent-first-data](https://github.com/cmnspore/agent-first-data) repository, which also contains:
469
+
470
+ - **`spec/`** — Full AFDATA specification with suffix definitions, protocol format rules, and cross-language test fixtures
471
+ - **`skills/`** — AI coding agent skill for working with AFDATA conventions
472
+
473
+ To run tests, clone the full repository (tests use shared cross-language fixtures from `spec/fixtures/`):
474
+
475
+ ```bash
476
+ git clone https://github.com/cmnspore/agent-first-data
477
+ cd agent-first-data/python
478
+ python -m pytest
479
+ ```
480
+
443
481
  ## License
444
482
 
445
483
  MIT
@@ -1,15 +1,6 @@
1
- Metadata-Version: 2.4
2
- Name: agent-first-data
3
- Version: 0.2.3
4
- Summary: Agent-First Data (AFD) — suffix-driven output formatting and protocol templates for AI agents
5
- License-Expression: MIT
6
- Project-URL: Repository, https://github.com/cmnspore/agent-first-data
7
- Requires-Python: >=3.9
8
- Description-Content-Type: text/markdown
9
-
10
1
  # agent-first-data
11
2
 
12
- **Agent-First Data (AFD)** — Suffix-driven output formatting and protocol templates for AI agents.
3
+ **Agent-First Data (AFDATA)** — Suffix-driven output formatting and protocol templates for AI agents.
13
4
 
14
5
  The field name is the schema. Agents read `latency_ms` and know milliseconds, `api_key_secret` and know to redact, no external schema needed.
15
6
 
@@ -27,24 +18,39 @@ A backup tool invoked from the CLI — flags, env vars, and config all use the s
27
18
  API_KEY_SECRET=sk-1234 cloudback --timeout-s 30 --max-file-size-bytes 10737418240 /data/backup.tar.gz
28
19
  ```
29
20
 
30
- The tool reads env vars, flags, and config — all with AFD suffixes — and emits a startup message:
21
+ For CLI diagnostics, enable log categories explicitly:
22
+
23
+ ```bash
24
+ --log startup,request,progress,retry,redirect
25
+ --verbose # shorthand for all categories
26
+ ```
27
+
28
+ Without these flags, startup diagnostics should stay off by default.
29
+
30
+ The tool reads env vars, flags, and config — all with AFDATA suffixes — and can emit a startup diagnostic event:
31
31
 
32
32
  ```python
33
33
  from agent_first_data import *
34
34
  import os
35
35
 
36
- startup = build_json_startup(
37
- {"timeout_s": 30, "max_file_size_bytes": 10737418240},
38
- {"input_path": "/data/backup.tar.gz"},
39
- {"API_KEY_SECRET": os.environ.get("API_KEY_SECRET")},
36
+ startup = build_json(
37
+ "log",
38
+ {
39
+ "event": "startup",
40
+ "config": {"timeout_s": 30, "max_file_size_bytes": 10737418240},
41
+ "args": {"input_path": "/data/backup.tar.gz"},
42
+ "env": {"API_KEY_SECRET": os.environ.get("API_KEY_SECRET")},
43
+ },
44
+ trace=None,
40
45
  )
41
46
  ```
42
47
 
43
48
  Three output formats, same data:
44
49
 
45
50
  ```
46
- JSON: {"code":"startup","args":{"input_path":"/data/backup.tar.gz"},"config":{"max_file_size_bytes":10737418240,"timeout_s":30},"env":{"API_KEY_SECRET":"***"}}
47
- YAML: code: "startup"
51
+ JSON: {"code":"log","event":"startup","args":{"input_path":"/data/backup.tar.gz"},"config":{"max_file_size_bytes":10737418240,"timeout_s":30},"env":{"API_KEY_SECRET":"***"}}
52
+ YAML: code: "log"
53
+ event: "startup"
48
54
  args:
49
55
  input_path: "/data/backup.tar.gz"
50
56
  config:
@@ -52,23 +58,20 @@ YAML: code: "startup"
52
58
  timeout: "30s"
53
59
  env:
54
60
  API_KEY: "***"
55
- Plain: args.input_path=/data/backup.tar.gz code=startup config.max_file_size=10.0GB config.timeout=30s env.API_KEY=***
61
+ Plain: args.input_path=/data/backup.tar.gz code=log event=startup config.max_file_size=10.0GB config.timeout=30s env.API_KEY=***
56
62
  ```
57
63
 
58
64
  `--timeout-s` → `timeout_s` → `timeout: 30s`. `API_KEY_SECRET` → `API_KEY: "***"`. The suffix is the schema.
59
65
 
60
66
  ## API Reference
61
67
 
62
- Total: **9 public APIs** + **AFD logging** (4 protocol builders + 3 output functions + 1 internal + 1 utility)
68
+ Total: **8 public APIs** + **AFDATA logging** (3 protocol builders + 3 output functions + 1 internal + 1 utility)
63
69
 
64
70
  ### Protocol Builders (returns dict)
65
71
 
66
- Build AFD protocol structures. Return dict objects for API responses.
72
+ Build AFDATA protocol structures. Return dict objects for API responses.
67
73
 
68
74
  ```python
69
- # Startup (configuration)
70
- build_json_startup(config: Any, args: Any, env: Any) -> dict
71
-
72
75
  # Success (result)
73
76
  build_json_ok(result: Any, trace: Any = None) -> dict
74
77
 
@@ -86,10 +89,15 @@ build_json(code: str, fields: Any, trace: Any = None) -> dict
86
89
  from agent_first_data import *
87
90
 
88
91
  # Startup
89
- startup = build_json_startup(
90
- {"api_key_secret": "sk-123", "timeout_s": 30},
91
- {"config_path": "config.yml"},
92
- {"RUST_LOG": "info"},
92
+ startup = build_json(
93
+ "log",
94
+ {
95
+ "event": "startup",
96
+ "config": {"api_key_secret": "sk-123", "timeout_s": 30},
97
+ "args": {"config_path": "config.yml"},
98
+ "env": {"RUST_LOG": "info"},
99
+ },
100
+ trace=None,
93
101
  )
94
102
 
95
103
  # Success (always include trace)
@@ -196,14 +204,20 @@ async def get_user(user_id: int):
196
204
  from agent_first_data import *
197
205
 
198
206
  # 1. Startup
199
- startup = build_json_startup(
200
- {"api_key_secret": "sk-sensitive-key", "timeout_s": 30},
201
- {"input_path": "data.json"},
202
- {"RUST_LOG": "info"},
207
+ startup = build_json(
208
+ "log",
209
+ {
210
+ "event": "startup",
211
+ "config": {"api_key_secret": "sk-sensitive-key", "timeout_s": 30},
212
+ "args": {"input_path": "data.json"},
213
+ "env": {"RUST_LOG": "info"},
214
+ },
215
+ trace=None,
203
216
  )
204
217
  print(output_yaml(startup))
205
218
  # ---
206
- # code: "startup"
219
+ # code: "log"
220
+ # event: "startup"
207
221
  # args:
208
222
  # input_path: "data.json"
209
223
  # config:
@@ -294,23 +308,23 @@ print(output_plain(data))
294
308
  # api_key=*** cache_ttl=3600s count=42 created_at=2025-02-07T00:00:00.000Z file_size=5.0MB payment=50000000msats price=$99.99 request_timeout=5.0s success_rate=95.5% user_name=alice
295
309
  ```
296
310
 
297
- ## AFD Logging
311
+ ## AFDATA Logging
298
312
 
299
- AFD-compliant structured logging via Python's `logging` module. Every log line is formatted using the library's own `output_json`/`output_plain`/`output_yaml` functions. Span fields are carried via `contextvars` (async-safe), automatically flattened into each log line.
313
+ AFDATA-compliant structured logging via Python's `logging` module. Every log line is formatted using the library's own `output_json`/`output_plain`/`output_yaml` functions. Span fields are carried via `contextvars` (async-safe), automatically flattened into each log line.
300
314
 
301
315
  ### API
302
316
 
303
317
  ```python
304
318
  from agent_first_data import init_logging_json, init_logging_plain, init_logging_yaml
305
- from agent_first_data.afd_logging import AfdHandler, get_logger, span
319
+ from agent_first_data.afdata_logging import AfdataHandler, get_logger, span
306
320
 
307
- # Convenience initializers — set up the root logger with AFD output to stdout
321
+ # Convenience initializers — set up the root logger with AFDATA output to stdout
308
322
  init_logging_json(level="INFO") # Single-line JSONL (secrets redacted, original keys)
309
323
  init_logging_plain(level="INFO") # Single-line logfmt (keys stripped, values formatted)
310
324
  init_logging_yaml(level="INFO") # Multi-line YAML (keys stripped, values formatted)
311
325
 
312
326
  # Low-level — create a handler for custom logger stacks
313
- AfdHandler(format="json") # format: "json" | "plain" | "yaml"
327
+ AfdataHandler(format="json") # format: "json" | "plain" | "yaml"
314
328
 
315
329
  # Logger with default fields (returns logging.LoggerAdapter)
316
330
  get_logger(name, **fields)
@@ -391,8 +405,8 @@ The `code` field defaults to the log level. Override with an explicit field:
391
405
  from agent_first_data import get_logger
392
406
 
393
407
  logger = get_logger("myapp")
394
- logger.info("Server ready", extra={"code": "startup"})
395
- # {"timestamp_epoch_ms":...,"message":"Server ready","target":"myapp","code":"startup"}
408
+ logger.info("Server ready", extra={"code": "log", "event": "startup"})
409
+ # {"timestamp_epoch_ms":...,"message":"Server ready","target":"myapp","code":"log","event":"startup"}
396
410
  ```
397
411
 
398
412
  ### Output Fields
@@ -410,7 +424,7 @@ Every log line contains:
410
424
 
411
425
  ### Log Output Formats
412
426
 
413
- All three formats use the library's own output functions, so AFD suffix processing applies to log fields too:
427
+ All three formats use the library's own output functions, so AFDATA suffix processing applies to log fields too:
414
428
 
415
429
  | Format | Function | Keys | Values | Use case |
416
430
  |:-------|:---------|:-----|:-------|:---------|
@@ -440,6 +454,21 @@ All formats automatically redact `_secret` fields.
440
454
  - **Currency**: `_msats`, `_sats`, `_btc`, `_usd_cents`, `_eur_cents`, `_jpy`, `_{code}_cents`
441
455
  - **Other**: `_percent`, `_secret` (auto-redacted in all formats)
442
456
 
457
+ ## Repository
458
+
459
+ This package is part of the [agent-first-data](https://github.com/cmnspore/agent-first-data) repository, which also contains:
460
+
461
+ - **`spec/`** — Full AFDATA specification with suffix definitions, protocol format rules, and cross-language test fixtures
462
+ - **`skills/`** — AI coding agent skill for working with AFDATA conventions
463
+
464
+ To run tests, clone the full repository (tests use shared cross-language fixtures from `spec/fixtures/`):
465
+
466
+ ```bash
467
+ git clone https://github.com/cmnspore/agent-first-data
468
+ cd agent-first-data/python
469
+ python -m pytest
470
+ ```
471
+
443
472
  ## License
444
473
 
445
474
  MIT
@@ -1,7 +1,6 @@
1
- """Agent-First Data (AFD) — suffix-driven output formatting and protocol templates."""
1
+ """Agent-First Data (AFDATA) — suffix-driven output formatting and protocol templates."""
2
2
 
3
3
  from agent_first_data.format import (
4
- build_json_startup,
5
4
  build_json_ok,
6
5
  build_json_error,
7
6
  build_json,
@@ -12,9 +11,9 @@ from agent_first_data.format import (
12
11
  parse_size,
13
12
  )
14
13
 
15
- from agent_first_data.afd_logging import (
16
- AfdHandler,
17
- AfdJsonHandler,
14
+ from agent_first_data.afdata_logging import (
15
+ AfdataHandler,
16
+ AfdataJsonHandler,
18
17
  init_json as init_logging_json,
19
18
  init_plain as init_logging_plain,
20
19
  init_yaml as init_logging_yaml,
@@ -23,7 +22,6 @@ from agent_first_data.afd_logging import (
23
22
  )
24
23
 
25
24
  __all__ = [
26
- "build_json_startup",
27
25
  "build_json_ok",
28
26
  "build_json_error",
29
27
  "build_json",
@@ -32,8 +30,8 @@ __all__ = [
32
30
  "output_plain",
33
31
  "internal_redact_secrets",
34
32
  "parse_size",
35
- "AfdHandler",
36
- "AfdJsonHandler",
33
+ "AfdataHandler",
34
+ "AfdataJsonHandler",
37
35
  "init_logging_json",
38
36
  "init_logging_plain",
39
37
  "init_logging_yaml",
@@ -1,4 +1,4 @@
1
- """AFD-compliant structured logging.
1
+ """AFDATA-compliant structured logging.
2
2
 
3
3
  Outputs log events using agent-first-data formatting functions:
4
4
  - JSON: single-line JSONL via output_json (secrets redacted, original keys)
@@ -8,7 +8,7 @@ Outputs log events using agent-first-data formatting functions:
8
8
  Span fields are carried via contextvars (async-safe).
9
9
 
10
10
  Usage:
11
- from agent_first_data.afd_logging import init_json, init_plain, init_yaml, span
11
+ from agent_first_data.afdata_logging import init_json, init_plain, init_yaml, span
12
12
  import logging
13
13
 
14
14
  init_json("INFO") # or init_plain("INFO") or init_yaml("DEBUG")
@@ -25,7 +25,7 @@ from typing import Any
25
25
 
26
26
  from agent_first_data.format import output_json, output_plain, output_yaml
27
27
 
28
- _span_fields: ContextVar[dict[str, Any]] = ContextVar("afd_span", default={})
28
+ _span_fields: ContextVar[dict[str, Any]] = ContextVar("afdata_span", default={})
29
29
 
30
30
  _LEVEL_TO_CODE = {
31
31
  "CRITICAL": "error",
@@ -38,8 +38,8 @@ _LEVEL_TO_CODE = {
38
38
  }
39
39
 
40
40
 
41
- class AfdHandler(logging.Handler):
42
- """Logging handler that outputs AFD-compliant log lines to stdout.
41
+ class AfdataHandler(logging.Handler):
42
+ """Logging handler that outputs AFDATA-compliant log lines to stdout.
43
43
 
44
44
  Formats output using the library's own output_json/output_plain/output_yaml.
45
45
  """
@@ -64,7 +64,7 @@ class AfdHandler(logging.Handler):
64
64
 
65
65
  # Event fields (passed via extra= in logging calls)
66
66
  has_code = False
67
- extra = getattr(record, "_afd_fields", None)
67
+ extra = getattr(record, "_afdata_fields", None)
68
68
  if extra:
69
69
  for k, v in extra.items():
70
70
  if k == "code":
@@ -88,11 +88,11 @@ class AfdHandler(logging.Handler):
88
88
 
89
89
 
90
90
  # Keep old name as alias for backwards compat
91
- AfdJsonHandler = AfdHandler
91
+ AfdataJsonHandler = AfdataHandler
92
92
 
93
93
 
94
- class _AfdLoggerAdapter(logging.LoggerAdapter):
95
- """Logger adapter that passes extra fields to AfdHandler."""
94
+ class _AfdataLoggerAdapter(logging.LoggerAdapter):
95
+ """Logger adapter that passes extra fields to AfdataHandler."""
96
96
 
97
97
  def process(self, msg: str, kwargs: Any) -> tuple[str, Any]:
98
98
  extra = kwargs.get("extra", {})
@@ -100,29 +100,29 @@ class _AfdLoggerAdapter(logging.LoggerAdapter):
100
100
  merged = {**self.extra, **extra}
101
101
  else:
102
102
  merged = extra
103
- kwargs["extra"] = {"_afd_fields": merged}
103
+ kwargs["extra"] = {"_afdata_fields": merged}
104
104
  return msg, kwargs
105
105
 
106
106
 
107
107
  def _init_with_format(format: str, level: str = "INFO") -> None:
108
- handler = AfdHandler(format=format)
108
+ handler = AfdataHandler(format=format)
109
109
  root = logging.getLogger()
110
110
  root.handlers = [handler]
111
111
  root.setLevel(getattr(logging, level.upper(), logging.INFO))
112
112
 
113
113
 
114
114
  def init_json(level: str = "INFO") -> None:
115
- """Initialize the root logger with AFD JSON output to stdout."""
115
+ """Initialize the root logger with AFDATA JSON output to stdout."""
116
116
  _init_with_format("json", level)
117
117
 
118
118
 
119
119
  def init_plain(level: str = "INFO") -> None:
120
- """Initialize the root logger with AFD plain/logfmt output to stdout."""
120
+ """Initialize the root logger with AFDATA plain/logfmt output to stdout."""
121
121
  _init_with_format("plain", level)
122
122
 
123
123
 
124
124
  def init_yaml(level: str = "INFO") -> None:
125
- """Initialize the root logger with AFD YAML output to stdout."""
125
+ """Initialize the root logger with AFDATA YAML output to stdout."""
126
126
  _init_with_format("yaml", level)
127
127
 
128
128
 
@@ -133,7 +133,7 @@ def get_logger(name: str, **fields: Any) -> logging.LoggerAdapter:
133
133
  Use for per-module or per-component fields.
134
134
  """
135
135
  base = logging.getLogger(name)
136
- return _AfdLoggerAdapter(base, fields)
136
+ return _AfdataLoggerAdapter(base, fields)
137
137
 
138
138
 
139
139
  class span:
@@ -1,6 +1,6 @@
1
- """AFD output formatting and protocol templates.
1
+ """AFDATA output formatting and protocol templates.
2
2
 
3
- 9 public APIs: 4 protocol builders + 3 output formatters + 1 redaction + 1 utility.
3
+ 8 public APIs: 3 protocol builders + 3 output formatters + 1 redaction + 1 utility.
4
4
  """
5
5
 
6
6
  from __future__ import annotations
@@ -16,11 +16,6 @@ from typing import Any
16
16
  # ═══════════════════════════════════════════
17
17
 
18
18
 
19
- def build_json_startup(config: Any, args: Any, env: Any) -> dict:
20
- """Build {code: "startup", config, args, env}."""
21
- return {"code": "startup", "config": config, "args": args, "env": env}
22
-
23
-
24
19
  def build_json_ok(result: Any, trace: Any = None) -> dict:
25
20
  """Build {code: "ok", result, trace?}."""
26
21
  m: dict = {"code": "ok", "result": result}
@@ -1,6 +1,15 @@
1
+ Metadata-Version: 2.4
2
+ Name: agent-first-data
3
+ Version: 0.3.0
4
+ Summary: Agent-First Data (AFDATA) — suffix-driven output formatting and protocol templates for AI agents
5
+ License-Expression: MIT
6
+ Project-URL: Repository, https://github.com/cmnspore/agent-first-data
7
+ Requires-Python: >=3.9
8
+ Description-Content-Type: text/markdown
9
+
1
10
  # agent-first-data
2
11
 
3
- **Agent-First Data (AFD)** — Suffix-driven output formatting and protocol templates for AI agents.
12
+ **Agent-First Data (AFDATA)** — Suffix-driven output formatting and protocol templates for AI agents.
4
13
 
5
14
  The field name is the schema. Agents read `latency_ms` and know milliseconds, `api_key_secret` and know to redact, no external schema needed.
6
15
 
@@ -18,24 +27,39 @@ A backup tool invoked from the CLI — flags, env vars, and config all use the s
18
27
  API_KEY_SECRET=sk-1234 cloudback --timeout-s 30 --max-file-size-bytes 10737418240 /data/backup.tar.gz
19
28
  ```
20
29
 
21
- The tool reads env vars, flags, and config — all with AFD suffixes — and emits a startup message:
30
+ For CLI diagnostics, enable log categories explicitly:
31
+
32
+ ```bash
33
+ --log startup,request,progress,retry,redirect
34
+ --verbose # shorthand for all categories
35
+ ```
36
+
37
+ Without these flags, startup diagnostics should stay off by default.
38
+
39
+ The tool reads env vars, flags, and config — all with AFDATA suffixes — and can emit a startup diagnostic event:
22
40
 
23
41
  ```python
24
42
  from agent_first_data import *
25
43
  import os
26
44
 
27
- startup = build_json_startup(
28
- {"timeout_s": 30, "max_file_size_bytes": 10737418240},
29
- {"input_path": "/data/backup.tar.gz"},
30
- {"API_KEY_SECRET": os.environ.get("API_KEY_SECRET")},
45
+ startup = build_json(
46
+ "log",
47
+ {
48
+ "event": "startup",
49
+ "config": {"timeout_s": 30, "max_file_size_bytes": 10737418240},
50
+ "args": {"input_path": "/data/backup.tar.gz"},
51
+ "env": {"API_KEY_SECRET": os.environ.get("API_KEY_SECRET")},
52
+ },
53
+ trace=None,
31
54
  )
32
55
  ```
33
56
 
34
57
  Three output formats, same data:
35
58
 
36
59
  ```
37
- JSON: {"code":"startup","args":{"input_path":"/data/backup.tar.gz"},"config":{"max_file_size_bytes":10737418240,"timeout_s":30},"env":{"API_KEY_SECRET":"***"}}
38
- YAML: code: "startup"
60
+ JSON: {"code":"log","event":"startup","args":{"input_path":"/data/backup.tar.gz"},"config":{"max_file_size_bytes":10737418240,"timeout_s":30},"env":{"API_KEY_SECRET":"***"}}
61
+ YAML: code: "log"
62
+ event: "startup"
39
63
  args:
40
64
  input_path: "/data/backup.tar.gz"
41
65
  config:
@@ -43,23 +67,20 @@ YAML: code: "startup"
43
67
  timeout: "30s"
44
68
  env:
45
69
  API_KEY: "***"
46
- Plain: args.input_path=/data/backup.tar.gz code=startup config.max_file_size=10.0GB config.timeout=30s env.API_KEY=***
70
+ Plain: args.input_path=/data/backup.tar.gz code=log event=startup config.max_file_size=10.0GB config.timeout=30s env.API_KEY=***
47
71
  ```
48
72
 
49
73
  `--timeout-s` → `timeout_s` → `timeout: 30s`. `API_KEY_SECRET` → `API_KEY: "***"`. The suffix is the schema.
50
74
 
51
75
  ## API Reference
52
76
 
53
- Total: **9 public APIs** + **AFD logging** (4 protocol builders + 3 output functions + 1 internal + 1 utility)
77
+ Total: **8 public APIs** + **AFDATA logging** (3 protocol builders + 3 output functions + 1 internal + 1 utility)
54
78
 
55
79
  ### Protocol Builders (returns dict)
56
80
 
57
- Build AFD protocol structures. Return dict objects for API responses.
81
+ Build AFDATA protocol structures. Return dict objects for API responses.
58
82
 
59
83
  ```python
60
- # Startup (configuration)
61
- build_json_startup(config: Any, args: Any, env: Any) -> dict
62
-
63
84
  # Success (result)
64
85
  build_json_ok(result: Any, trace: Any = None) -> dict
65
86
 
@@ -77,10 +98,15 @@ build_json(code: str, fields: Any, trace: Any = None) -> dict
77
98
  from agent_first_data import *
78
99
 
79
100
  # Startup
80
- startup = build_json_startup(
81
- {"api_key_secret": "sk-123", "timeout_s": 30},
82
- {"config_path": "config.yml"},
83
- {"RUST_LOG": "info"},
101
+ startup = build_json(
102
+ "log",
103
+ {
104
+ "event": "startup",
105
+ "config": {"api_key_secret": "sk-123", "timeout_s": 30},
106
+ "args": {"config_path": "config.yml"},
107
+ "env": {"RUST_LOG": "info"},
108
+ },
109
+ trace=None,
84
110
  )
85
111
 
86
112
  # Success (always include trace)
@@ -187,14 +213,20 @@ async def get_user(user_id: int):
187
213
  from agent_first_data import *
188
214
 
189
215
  # 1. Startup
190
- startup = build_json_startup(
191
- {"api_key_secret": "sk-sensitive-key", "timeout_s": 30},
192
- {"input_path": "data.json"},
193
- {"RUST_LOG": "info"},
216
+ startup = build_json(
217
+ "log",
218
+ {
219
+ "event": "startup",
220
+ "config": {"api_key_secret": "sk-sensitive-key", "timeout_s": 30},
221
+ "args": {"input_path": "data.json"},
222
+ "env": {"RUST_LOG": "info"},
223
+ },
224
+ trace=None,
194
225
  )
195
226
  print(output_yaml(startup))
196
227
  # ---
197
- # code: "startup"
228
+ # code: "log"
229
+ # event: "startup"
198
230
  # args:
199
231
  # input_path: "data.json"
200
232
  # config:
@@ -285,23 +317,23 @@ print(output_plain(data))
285
317
  # api_key=*** cache_ttl=3600s count=42 created_at=2025-02-07T00:00:00.000Z file_size=5.0MB payment=50000000msats price=$99.99 request_timeout=5.0s success_rate=95.5% user_name=alice
286
318
  ```
287
319
 
288
- ## AFD Logging
320
+ ## AFDATA Logging
289
321
 
290
- AFD-compliant structured logging via Python's `logging` module. Every log line is formatted using the library's own `output_json`/`output_plain`/`output_yaml` functions. Span fields are carried via `contextvars` (async-safe), automatically flattened into each log line.
322
+ AFDATA-compliant structured logging via Python's `logging` module. Every log line is formatted using the library's own `output_json`/`output_plain`/`output_yaml` functions. Span fields are carried via `contextvars` (async-safe), automatically flattened into each log line.
291
323
 
292
324
  ### API
293
325
 
294
326
  ```python
295
327
  from agent_first_data import init_logging_json, init_logging_plain, init_logging_yaml
296
- from agent_first_data.afd_logging import AfdHandler, get_logger, span
328
+ from agent_first_data.afdata_logging import AfdataHandler, get_logger, span
297
329
 
298
- # Convenience initializers — set up the root logger with AFD output to stdout
330
+ # Convenience initializers — set up the root logger with AFDATA output to stdout
299
331
  init_logging_json(level="INFO") # Single-line JSONL (secrets redacted, original keys)
300
332
  init_logging_plain(level="INFO") # Single-line logfmt (keys stripped, values formatted)
301
333
  init_logging_yaml(level="INFO") # Multi-line YAML (keys stripped, values formatted)
302
334
 
303
335
  # Low-level — create a handler for custom logger stacks
304
- AfdHandler(format="json") # format: "json" | "plain" | "yaml"
336
+ AfdataHandler(format="json") # format: "json" | "plain" | "yaml"
305
337
 
306
338
  # Logger with default fields (returns logging.LoggerAdapter)
307
339
  get_logger(name, **fields)
@@ -382,8 +414,8 @@ The `code` field defaults to the log level. Override with an explicit field:
382
414
  from agent_first_data import get_logger
383
415
 
384
416
  logger = get_logger("myapp")
385
- logger.info("Server ready", extra={"code": "startup"})
386
- # {"timestamp_epoch_ms":...,"message":"Server ready","target":"myapp","code":"startup"}
417
+ logger.info("Server ready", extra={"code": "log", "event": "startup"})
418
+ # {"timestamp_epoch_ms":...,"message":"Server ready","target":"myapp","code":"log","event":"startup"}
387
419
  ```
388
420
 
389
421
  ### Output Fields
@@ -401,7 +433,7 @@ Every log line contains:
401
433
 
402
434
  ### Log Output Formats
403
435
 
404
- All three formats use the library's own output functions, so AFD suffix processing applies to log fields too:
436
+ All three formats use the library's own output functions, so AFDATA suffix processing applies to log fields too:
405
437
 
406
438
  | Format | Function | Keys | Values | Use case |
407
439
  |:-------|:---------|:-----|:-------|:---------|
@@ -431,6 +463,21 @@ All formats automatically redact `_secret` fields.
431
463
  - **Currency**: `_msats`, `_sats`, `_btc`, `_usd_cents`, `_eur_cents`, `_jpy`, `_{code}_cents`
432
464
  - **Other**: `_percent`, `_secret` (auto-redacted in all formats)
433
465
 
466
+ ## Repository
467
+
468
+ This package is part of the [agent-first-data](https://github.com/cmnspore/agent-first-data) repository, which also contains:
469
+
470
+ - **`spec/`** — Full AFDATA specification with suffix definitions, protocol format rules, and cross-language test fixtures
471
+ - **`skills/`** — AI coding agent skill for working with AFDATA conventions
472
+
473
+ To run tests, clone the full repository (tests use shared cross-language fixtures from `spec/fixtures/`):
474
+
475
+ ```bash
476
+ git clone https://github.com/cmnspore/agent-first-data
477
+ cd agent-first-data/python
478
+ python -m pytest
479
+ ```
480
+
434
481
  ## License
435
482
 
436
483
  MIT
@@ -1,11 +1,11 @@
1
1
  README.md
2
2
  pyproject.toml
3
3
  agent_first_data/__init__.py
4
- agent_first_data/afd_logging.py
4
+ agent_first_data/afdata_logging.py
5
5
  agent_first_data/format.py
6
6
  agent_first_data.egg-info/PKG-INFO
7
7
  agent_first_data.egg-info/SOURCES.txt
8
8
  agent_first_data.egg-info/dependency_links.txt
9
9
  agent_first_data.egg-info/top_level.txt
10
- tests/test_afd_logging.py
10
+ tests/test_afdata_logging.py
11
11
  tests/test_format.py
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "agent-first-data"
3
- version = "0.2.3"
4
- description = "Agent-First Data (AFD) — suffix-driven output formatting and protocol templates for AI agents"
3
+ version = "0.3.0"
4
+ description = "Agent-First Data (AFDATA) — suffix-driven output formatting and protocol templates for AI agents"
5
5
  license = "MIT"
6
6
  readme = "README.md"
7
7
  requires-python = ">=3.9"
@@ -1,4 +1,4 @@
1
- """Tests for AFD logging module."""
1
+ """Tests for AFDATA logging module."""
2
2
 
3
3
  import json
4
4
  import logging
@@ -6,7 +6,7 @@ import sys
6
6
  from io import StringIO
7
7
  from unittest.mock import patch
8
8
 
9
- from agent_first_data.afd_logging import AfdHandler, AfdJsonHandler, init_json, init_plain, init_yaml, span, get_logger
9
+ from agent_first_data.afdata_logging import AfdataHandler, AfdataJsonHandler, init_json, init_plain, init_yaml, span, get_logger
10
10
 
11
11
 
12
12
  def capture_log(fn):
@@ -20,9 +20,9 @@ def capture_log(fn):
20
20
 
21
21
 
22
22
  def make_logger(name="test"):
23
- """Create a fresh logger with AfdJsonHandler."""
23
+ """Create a fresh logger with AfdataJsonHandler."""
24
24
  logger = logging.getLogger(name)
25
- logger.handlers = [AfdJsonHandler()]
25
+ logger.handlers = [AfdataJsonHandler()]
26
26
  logger.setLevel(logging.DEBUG)
27
27
  return logger
28
28
 
@@ -108,15 +108,16 @@ class TestCodeOverride:
108
108
  logger = make_logger("test_code")
109
109
  adapter = get_logger("test_code")
110
110
 
111
- m = capture_log(lambda: adapter.info("ready", extra={"code": "startup"}))
112
- assert m["code"] == "startup"
111
+ m = capture_log(lambda: adapter.info("ready", extra={"code": "log", "event": "startup"}))
112
+ assert m["code"] == "log"
113
+ assert m["event"] == "startup"
113
114
 
114
115
 
115
116
  class TestGetLogger:
116
117
  def test_default_fields(self):
117
- # Ensure root logger has AfdJsonHandler
118
+ # Ensure root logger has AfdataJsonHandler
118
119
  root = logging.getLogger()
119
- root.handlers = [AfdJsonHandler()]
120
+ root.handlers = [AfdataJsonHandler()]
120
121
  root.setLevel(logging.DEBUG)
121
122
 
122
123
  adapter = get_logger("test_adapter", component="myservice")
@@ -137,7 +138,7 @@ def capture_raw(fn):
137
138
  class TestPlainFormat:
138
139
  def test_plain_output(self):
139
140
  logger = logging.getLogger("test_plain")
140
- logger.handlers = [AfdHandler(format="plain")]
141
+ logger.handlers = [AfdataHandler(format="plain")]
141
142
  logger.setLevel(logging.DEBUG)
142
143
 
143
144
  output = capture_raw(lambda: logger.info("hello"))
@@ -157,7 +158,7 @@ class TestPlainFormat:
157
158
  class TestYamlFormat:
158
159
  def test_yaml_output(self):
159
160
  logger = logging.getLogger("test_yaml")
160
- logger.handlers = [AfdHandler(format="yaml")]
161
+ logger.handlers = [AfdataHandler(format="yaml")]
161
162
  logger.setLevel(logging.DEBUG)
162
163
 
163
164
  output = capture_raw(lambda: logger.info("hello"))
@@ -1,10 +1,9 @@
1
- """Tests for AFD output formatting — driven by shared spec/fixtures."""
1
+ """Tests for AFDATA output formatting — driven by shared spec/fixtures."""
2
2
 
3
3
  import json
4
4
  import os
5
5
 
6
6
  from agent_first_data import (
7
- build_json_startup,
8
7
  build_json_ok,
9
8
  build_json_error,
10
9
  build_json,
@@ -52,8 +51,6 @@ def test_protocol_fixtures():
52
51
  result = build_json_error(args["message"])
53
52
  elif typ == "error_trace":
54
53
  result = build_json_error(args["message"], args["trace"])
55
- elif typ == "startup":
56
- result = build_json_startup(args["config"], args["args"], args["env"])
57
54
  elif typ == "status":
58
55
  result = build_json(args["code"], args.get("fields"))
59
56
  else: