deepdebug-local-agent 1.12.0 → 1.13.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 +2 -0
- package/docs/la-chat.md +35 -0
- package/package.json +1 -1
- package/src/dlp/rules.js +2 -2
- package/src/la-protocol-v2/chat/config.js +60 -0
- package/src/la-protocol-v2/chat/cursors.js +57 -0
- package/src/la-protocol-v2/chat/dlp.js +92 -0
- package/src/la-protocol-v2/chat/index.js +28 -0
- package/src/la-protocol-v2/chat/store.js +337 -0
- package/src/la-protocol-v2/chat/stream-source.js +23 -0
- package/src/la-protocol-v2/content-handlers/chat-session.js +41 -0
- package/src/la-protocol-v2/content-handlers/handler.js +10 -2
- package/src/la-protocol-v2/content-handlers/install.js +2 -2
- package/src/la-protocol-v2/content-handlers/job-stream.js +62 -0
- package/src/la-protocol-v2/contract/CONTRACT_SOURCE.json +13 -8
- package/src/la-protocol-v2/contract/README.md +42 -0
- package/src/la-protocol-v2/contract/chat.session.body.schema.json +47 -0
- package/src/la-protocol-v2/contract/index.json +1 -1
- package/src/la-protocol-v2/contract/job.assign.schema.json +3 -2
- package/src/la-protocol-v2/contract/job.stream.body.schema.json +49 -0
- package/src/la-protocol-v2/jobs/agent2/api-client.js +78 -51
- package/src/la-protocol-v2/jobs/agent2/config.js +1 -0
- package/src/la-protocol-v2/jobs/agent2/runner.js +92 -11
- package/src/la-protocol-v2/jobs/agent2/supervisor.js +2 -0
- package/src/la-protocol-v2/jobs/git-exec.js +5 -4
- package/src/la-protocol-v2/jobs/manager.js +4 -2
- package/src/la-protocol-v2/jobs/workspace-materializer.js +35 -12
- package/src/la-protocol-v2/runtime.js +32 -3
- package/src/workspace-manager.js +3 -2
- package/test/contract/la-protocol-v2/vectors/chat.session.body.vectors.json +10379 -0
- package/test/contract/la-protocol-v2/vectors/content.request.vectors.json +245 -0
- package/test/contract/la-protocol-v2/vectors/hello.vectors.json +112 -0
- package/test/contract/la-protocol-v2/vectors/job.assign.vectors.json +568 -3
- package/test/contract/la-protocol-v2/vectors/job.stream.body.vectors.json +4572 -0
- package/test/contract/la-protocol-v2/vectors/register.vectors.json +106 -0
- package/test/contract/la-protocol-v2/vectors/traces/valid-chat-job-and-stream.json +311 -0
- package/test/la-protocol-v2/chat-dlp-json.test.js +169 -0
- package/test/la-protocol-v2/chat-la.test.js +490 -0
- package/test/la-protocol-v2/chat-real-e2e.test.js +69 -0
- package/test/la-protocol-v2/chat-store.test.js +301 -0
- package/test/la-protocol-v2/contract-v21.test.js +2 -2
- package/test/la-protocol-v2/contract.test.js +6 -6
- package/test/la-protocol-v2/helpers/agent2-rig.js +2 -2
- package/test/la-protocol-v2/helpers/fake-agent2-cli.js +1 -0
- package/test/la-protocol-v2/helpers/fake-agent2.js +80 -5
- package/test/la-protocol-v2/traces.test.js +3 -3
- package/test/la-protocol-v2/workspace-materializer-hostile.test.js +105 -0
|
@@ -669,6 +669,112 @@
|
|
|
669
669
|
]
|
|
670
670
|
}
|
|
671
671
|
}
|
|
672
|
+
},
|
|
673
|
+
{
|
|
674
|
+
"name": "capabilities-job-chat-and-inline-16k",
|
|
675
|
+
"why": "2.3: job.chat and job.inline.16k are capability names like the others (no enum); both imply job.assign, enforced by the CP routing, not by the schema",
|
|
676
|
+
"expect": {
|
|
677
|
+
"valid": true
|
|
678
|
+
},
|
|
679
|
+
"message": {
|
|
680
|
+
"schemaVersion": 2,
|
|
681
|
+
"type": "register",
|
|
682
|
+
"msgId": "00000000-0000-4000-8000-000000000001",
|
|
683
|
+
"seq": 0,
|
|
684
|
+
"tenantId": "3f2b8c1e-5a4d-4e7b-9c21-0a1b2c3d4e5f",
|
|
685
|
+
"workspaceId": "ws-1",
|
|
686
|
+
"ts": "2026-10-29T10:00:00Z",
|
|
687
|
+
"agentId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
|
|
688
|
+
"payload": {
|
|
689
|
+
"protocol": 2,
|
|
690
|
+
"laVersion": "2.4.0",
|
|
691
|
+
"capabilities": [
|
|
692
|
+
"content.request",
|
|
693
|
+
"incident.detect",
|
|
694
|
+
"job.assign",
|
|
695
|
+
"job.chat",
|
|
696
|
+
"job.inline.16k",
|
|
697
|
+
"job.options"
|
|
698
|
+
],
|
|
699
|
+
"signatureMode": "enforce",
|
|
700
|
+
"workspaces": [
|
|
701
|
+
{
|
|
702
|
+
"workspaceId": "ws-1",
|
|
703
|
+
"pathSha256": "abababababababababababababababababababababababababababababababab"
|
|
704
|
+
}
|
|
705
|
+
]
|
|
706
|
+
}
|
|
707
|
+
}
|
|
708
|
+
},
|
|
709
|
+
{
|
|
710
|
+
"name": "capability-job-chat-upper-case",
|
|
711
|
+
"expect": {
|
|
712
|
+
"valid": false,
|
|
713
|
+
"code": "schema_violation",
|
|
714
|
+
"keyword": "pattern"
|
|
715
|
+
},
|
|
716
|
+
"message": {
|
|
717
|
+
"schemaVersion": 2,
|
|
718
|
+
"type": "register",
|
|
719
|
+
"msgId": "00000000-0000-4000-8000-000000000001",
|
|
720
|
+
"seq": 0,
|
|
721
|
+
"tenantId": "3f2b8c1e-5a4d-4e7b-9c21-0a1b2c3d4e5f",
|
|
722
|
+
"workspaceId": "ws-1",
|
|
723
|
+
"ts": "2026-10-29T10:00:00Z",
|
|
724
|
+
"agentId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
|
|
725
|
+
"payload": {
|
|
726
|
+
"protocol": 2,
|
|
727
|
+
"laVersion": "2.4.0",
|
|
728
|
+
"capabilities": [
|
|
729
|
+
"content.request",
|
|
730
|
+
"job.assign",
|
|
731
|
+
"incident.detect",
|
|
732
|
+
"Job.Chat"
|
|
733
|
+
],
|
|
734
|
+
"signatureMode": "enforce",
|
|
735
|
+
"workspaces": [
|
|
736
|
+
{
|
|
737
|
+
"workspaceId": "ws-1",
|
|
738
|
+
"pathSha256": "abababababababababababababababababababababababababababababababab"
|
|
739
|
+
}
|
|
740
|
+
]
|
|
741
|
+
}
|
|
742
|
+
}
|
|
743
|
+
},
|
|
744
|
+
{
|
|
745
|
+
"name": "capability-job-inline-16k-with-space",
|
|
746
|
+
"expect": {
|
|
747
|
+
"valid": false,
|
|
748
|
+
"code": "schema_violation",
|
|
749
|
+
"keyword": "pattern"
|
|
750
|
+
},
|
|
751
|
+
"message": {
|
|
752
|
+
"schemaVersion": 2,
|
|
753
|
+
"type": "register",
|
|
754
|
+
"msgId": "00000000-0000-4000-8000-000000000001",
|
|
755
|
+
"seq": 0,
|
|
756
|
+
"tenantId": "3f2b8c1e-5a4d-4e7b-9c21-0a1b2c3d4e5f",
|
|
757
|
+
"workspaceId": "ws-1",
|
|
758
|
+
"ts": "2026-10-29T10:00:00Z",
|
|
759
|
+
"agentId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
|
|
760
|
+
"payload": {
|
|
761
|
+
"protocol": 2,
|
|
762
|
+
"laVersion": "2.4.0",
|
|
763
|
+
"capabilities": [
|
|
764
|
+
"content.request",
|
|
765
|
+
"job.assign",
|
|
766
|
+
"incident.detect",
|
|
767
|
+
"job.inline 16k"
|
|
768
|
+
],
|
|
769
|
+
"signatureMode": "enforce",
|
|
770
|
+
"workspaces": [
|
|
771
|
+
{
|
|
772
|
+
"workspaceId": "ws-1",
|
|
773
|
+
"pathSha256": "abababababababababababababababababababababababababababababababab"
|
|
774
|
+
}
|
|
775
|
+
]
|
|
776
|
+
}
|
|
777
|
+
}
|
|
672
778
|
}
|
|
673
779
|
]
|
|
674
780
|
}
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "valid-chat-job-and-stream",
|
|
3
|
+
"why": "2.3: register/hello announce job.chat and job.inline.16k; job.assign kind chat with a 16384-char transient inline; job.stream long-poll answered with a job.stream body; the job ends no_change (a chat opens no PR)",
|
|
4
|
+
"session": {
|
|
5
|
+
"tenantId": "3f2b8c1e-5a4d-4e7b-9c21-0a1b2c3d4e5f",
|
|
6
|
+
"agentId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
|
|
7
|
+
"requireEnforce": true,
|
|
8
|
+
"now": "2026-10-29T10:00:00Z"
|
|
9
|
+
},
|
|
10
|
+
"frames": [
|
|
11
|
+
{
|
|
12
|
+
"dir": "la->cp",
|
|
13
|
+
"frame": {
|
|
14
|
+
"schemaVersion": 2,
|
|
15
|
+
"type": "register",
|
|
16
|
+
"msgId": "00000000-0000-4000-8000-000000009000",
|
|
17
|
+
"seq": 0,
|
|
18
|
+
"tenantId": "3f2b8c1e-5a4d-4e7b-9c21-0a1b2c3d4e5f",
|
|
19
|
+
"workspaceId": "ws-1",
|
|
20
|
+
"ts": "2026-10-29T10:00:00Z",
|
|
21
|
+
"agentId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
|
|
22
|
+
"payload": {
|
|
23
|
+
"protocol": 2,
|
|
24
|
+
"laVersion": "2.4.0",
|
|
25
|
+
"capabilities": [
|
|
26
|
+
"content.request",
|
|
27
|
+
"job.assign",
|
|
28
|
+
"job.options",
|
|
29
|
+
"job.chat",
|
|
30
|
+
"job.inline.16k"
|
|
31
|
+
],
|
|
32
|
+
"signatureMode": "enforce",
|
|
33
|
+
"workspaces": [
|
|
34
|
+
{
|
|
35
|
+
"workspaceId": "ws-1",
|
|
36
|
+
"pathSha256": "abababababababababababababababababababababababababababababababab"
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
"dir": "la->cp",
|
|
44
|
+
"frame": {
|
|
45
|
+
"schemaVersion": 2,
|
|
46
|
+
"type": "hello",
|
|
47
|
+
"msgId": "00000000-0000-4000-8000-000000009000",
|
|
48
|
+
"seq": 0,
|
|
49
|
+
"tenantId": "3f2b8c1e-5a4d-4e7b-9c21-0a1b2c3d4e5f",
|
|
50
|
+
"workspaceId": "ws-1",
|
|
51
|
+
"ts": "2026-10-29T10:00:00Z",
|
|
52
|
+
"agentId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
|
|
53
|
+
"payload": {
|
|
54
|
+
"laVersion": "2.4.0",
|
|
55
|
+
"capabilities": [
|
|
56
|
+
"content.request",
|
|
57
|
+
"job.assign",
|
|
58
|
+
"job.options",
|
|
59
|
+
"job.chat",
|
|
60
|
+
"job.inline.16k"
|
|
61
|
+
],
|
|
62
|
+
"signatureMode": "enforce",
|
|
63
|
+
"graphify": {
|
|
64
|
+
"state": "ready",
|
|
65
|
+
"nodes": 12840,
|
|
66
|
+
"edges": 40211,
|
|
67
|
+
"commit": "9fceb02d",
|
|
68
|
+
"ageSeconds": 300
|
|
69
|
+
},
|
|
70
|
+
"diskFreeBytes": 52428800000,
|
|
71
|
+
"queueDepth": 0,
|
|
72
|
+
"clockEpochMs": 1793268000000
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
"dir": "cp->la",
|
|
78
|
+
"frame": {
|
|
79
|
+
"schemaVersion": 2,
|
|
80
|
+
"type": "ready",
|
|
81
|
+
"msgId": "00000000-0000-4000-8000-000000009000",
|
|
82
|
+
"seq": 0,
|
|
83
|
+
"tenantId": "3f2b8c1e-5a4d-4e7b-9c21-0a1b2c3d4e5f",
|
|
84
|
+
"workspaceId": "ws-1",
|
|
85
|
+
"ts": "2026-10-29T10:00:00Z",
|
|
86
|
+
"paramsRaw": "{\"protocol\":2,\"sessionId\":\"e5f6a7b8-c9d0-4e1f-8a2b-4c5d6e7f8091\",\"resumed\":false,\"peerLastSeq\":0,\"limits\":{\"maxFrameBytes\":49152,\"chunkRawBytes\":32768,\"maxChunks\":64,\"ackTimeoutMs\":5000,\"maxRetries\":3,\"heartbeatSeconds\":30,\"contentFirstByteMs\":10000,\"contentTotalMs\":30000}}",
|
|
87
|
+
"sig": {
|
|
88
|
+
"keyId": "vh-cp-2026-10",
|
|
89
|
+
"timestamp": "1793268000",
|
|
90
|
+
"nonce": "Zm9vYmFyYmF6cXV4MTIzNDU2",
|
|
91
|
+
"tenantId": "3f2b8c1e-5a4d-4e7b-9c21-0a1b2c3d4e5f",
|
|
92
|
+
"workspaceId": "ws-1",
|
|
93
|
+
"signature": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
|
|
94
|
+
},
|
|
95
|
+
"payload": {
|
|
96
|
+
"protocol": 2,
|
|
97
|
+
"sessionId": "e5f6a7b8-c9d0-4e1f-8a2b-4c5d6e7f8091",
|
|
98
|
+
"resumed": false,
|
|
99
|
+
"peerLastSeq": 0,
|
|
100
|
+
"limits": {
|
|
101
|
+
"maxFrameBytes": 49152,
|
|
102
|
+
"chunkRawBytes": 32768,
|
|
103
|
+
"maxChunks": 64,
|
|
104
|
+
"ackTimeoutMs": 5000,
|
|
105
|
+
"maxRetries": 3,
|
|
106
|
+
"heartbeatSeconds": 30,
|
|
107
|
+
"contentFirstByteMs": 10000,
|
|
108
|
+
"contentTotalMs": 30000
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"dir": "cp->la",
|
|
115
|
+
"frame": {
|
|
116
|
+
"schemaVersion": 2,
|
|
117
|
+
"type": "job.assign",
|
|
118
|
+
"msgId": "00000000-0000-4000-8000-000000000001",
|
|
119
|
+
"seq": 1,
|
|
120
|
+
"tenantId": "3f2b8c1e-5a4d-4e7b-9c21-0a1b2c3d4e5f",
|
|
121
|
+
"workspaceId": "ws-1",
|
|
122
|
+
"ts": "2026-10-29T10:00:00Z",
|
|
123
|
+
"paramsRaw": "{\"jobId\":\"c3d4e5f6-a7b8-4c9d-8e0f-2a3b4c5d6e7f\",\"kind\":\"chat\",\"repoRef\":{\"provider\":\"github\",\"org\":\"acme\",\"repo\":\"billing\",\"branch\":\"main\"},\"policy\":{\"operationMode\":\"assist\",\"automationLevel\":2,\"ceiling\":\"autonomous\"},\"budget\":{\"tokens\":200000,\"seconds\":900},\"llm\":{\"alias\":\"vh-coder\",\"keyRef\":\"key-la-1\"},\"inputs\":[{\"inline\":\"Explain the retry logic of PaymentService. xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx\",\"transient\":true}]}",
|
|
124
|
+
"sig": {
|
|
125
|
+
"keyId": "vh-cp-2026-10",
|
|
126
|
+
"timestamp": "1793268000",
|
|
127
|
+
"nonce": "Zm9vYmFyYmF6cXV4MTIzNDU2",
|
|
128
|
+
"tenantId": "3f2b8c1e-5a4d-4e7b-9c21-0a1b2c3d4e5f",
|
|
129
|
+
"workspaceId": "ws-1",
|
|
130
|
+
"signature": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
|
|
131
|
+
},
|
|
132
|
+
"requestId": "d4e5f6a7-b8c9-4d0e-9f1a-3b4c5d6e7f80",
|
|
133
|
+
"payload": {
|
|
134
|
+
"jobId": "c3d4e5f6-a7b8-4c9d-8e0f-2a3b4c5d6e7f",
|
|
135
|
+
"kind": "chat",
|
|
136
|
+
"repoRef": {
|
|
137
|
+
"provider": "github",
|
|
138
|
+
"org": "acme",
|
|
139
|
+
"repo": "billing",
|
|
140
|
+
"branch": "main"
|
|
141
|
+
},
|
|
142
|
+
"policy": {
|
|
143
|
+
"operationMode": "assist",
|
|
144
|
+
"automationLevel": 2,
|
|
145
|
+
"ceiling": "autonomous"
|
|
146
|
+
},
|
|
147
|
+
"budget": {
|
|
148
|
+
"tokens": 200000,
|
|
149
|
+
"seconds": 900
|
|
150
|
+
},
|
|
151
|
+
"llm": {
|
|
152
|
+
"alias": "vh-coder",
|
|
153
|
+
"keyRef": "key-la-1"
|
|
154
|
+
},
|
|
155
|
+
"inputs": [
|
|
156
|
+
{
|
|
157
|
+
"inline": "Explain the retry logic of PaymentService. xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
|
158
|
+
"transient": true
|
|
159
|
+
}
|
|
160
|
+
]
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
{
|
|
165
|
+
"dir": "cp->la",
|
|
166
|
+
"frame": {
|
|
167
|
+
"schemaVersion": 2,
|
|
168
|
+
"type": "content.request",
|
|
169
|
+
"msgId": "00000000-0000-4000-8000-000000000002",
|
|
170
|
+
"seq": 2,
|
|
171
|
+
"tenantId": "3f2b8c1e-5a4d-4e7b-9c21-0a1b2c3d4e5f",
|
|
172
|
+
"workspaceId": "ws-1",
|
|
173
|
+
"ts": "2026-10-29T10:00:00Z",
|
|
174
|
+
"paramsRaw": "{\"kind\":\"job.stream\",\"ref\":\"c3d4e5f6-a7b8-4c9d-8e0f-2a3b4c5d6e7f\",\"actor\":{\"userId\":\"usr-42\",\"role\":\"member\"}}",
|
|
175
|
+
"sig": {
|
|
176
|
+
"keyId": "vh-cp-2026-10",
|
|
177
|
+
"timestamp": "1793268000",
|
|
178
|
+
"nonce": "Zm9vYmFyYmF6cXV4MTIzNDU2",
|
|
179
|
+
"tenantId": "3f2b8c1e-5a4d-4e7b-9c21-0a1b2c3d4e5f",
|
|
180
|
+
"workspaceId": "ws-1",
|
|
181
|
+
"signature": "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=="
|
|
182
|
+
},
|
|
183
|
+
"requestId": "e5f6a7b8-c9d0-4e1f-8a2b-4c5d6e7f8091",
|
|
184
|
+
"payload": {
|
|
185
|
+
"kind": "job.stream",
|
|
186
|
+
"ref": "c3d4e5f6-a7b8-4c9d-8e0f-2a3b4c5d6e7f",
|
|
187
|
+
"actor": {
|
|
188
|
+
"userId": "usr-42",
|
|
189
|
+
"role": "member"
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
},
|
|
194
|
+
{
|
|
195
|
+
"dir": "la->cp",
|
|
196
|
+
"frame": {
|
|
197
|
+
"schemaVersion": 2,
|
|
198
|
+
"type": "ack",
|
|
199
|
+
"msgId": "00000000-0000-4000-8000-000000009000",
|
|
200
|
+
"seq": 0,
|
|
201
|
+
"tenantId": "3f2b8c1e-5a4d-4e7b-9c21-0a1b2c3d4e5f",
|
|
202
|
+
"workspaceId": "ws-1",
|
|
203
|
+
"ts": "2026-10-29T10:00:00Z",
|
|
204
|
+
"payload": {
|
|
205
|
+
"ackSeq": 1,
|
|
206
|
+
"status": "accepted"
|
|
207
|
+
},
|
|
208
|
+
"agentId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d"
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
{
|
|
212
|
+
"dir": "la->cp",
|
|
213
|
+
"frame": {
|
|
214
|
+
"schemaVersion": 2,
|
|
215
|
+
"type": "job.event",
|
|
216
|
+
"msgId": "00000000-0000-4000-8000-000000000001",
|
|
217
|
+
"seq": 1,
|
|
218
|
+
"tenantId": "3f2b8c1e-5a4d-4e7b-9c21-0a1b2c3d4e5f",
|
|
219
|
+
"workspaceId": "ws-1",
|
|
220
|
+
"ts": "2026-10-29T10:00:00Z",
|
|
221
|
+
"agentId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
|
|
222
|
+
"payload": {
|
|
223
|
+
"jobId": "c3d4e5f6-a7b8-4c9d-8e0f-2a3b4c5d6e7f",
|
|
224
|
+
"state": "running",
|
|
225
|
+
"phase": "edit",
|
|
226
|
+
"iteration": 2,
|
|
227
|
+
"filesRead": 14,
|
|
228
|
+
"filesChanged": 1,
|
|
229
|
+
"tokensIn": 8200,
|
|
230
|
+
"tokensOut": 1300,
|
|
231
|
+
"costUsd": 0.042,
|
|
232
|
+
"durationMs": 61000,
|
|
233
|
+
"errorCode": "none"
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
"dir": "la->cp",
|
|
239
|
+
"frame": {
|
|
240
|
+
"schemaVersion": 2,
|
|
241
|
+
"type": "job.event",
|
|
242
|
+
"msgId": "00000000-0000-4000-8000-000000000002",
|
|
243
|
+
"seq": 2,
|
|
244
|
+
"tenantId": "3f2b8c1e-5a4d-4e7b-9c21-0a1b2c3d4e5f",
|
|
245
|
+
"workspaceId": "ws-1",
|
|
246
|
+
"ts": "2026-10-29T10:00:00Z",
|
|
247
|
+
"agentId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
|
|
248
|
+
"payload": {
|
|
249
|
+
"jobId": "c3d4e5f6-a7b8-4c9d-8e0f-2a3b4c5d6e7f",
|
|
250
|
+
"state": "running",
|
|
251
|
+
"phase": "edit",
|
|
252
|
+
"iteration": 2,
|
|
253
|
+
"filesRead": 14,
|
|
254
|
+
"filesChanged": 1,
|
|
255
|
+
"tokensIn": 8200,
|
|
256
|
+
"tokensOut": 1300,
|
|
257
|
+
"costUsd": 0.042,
|
|
258
|
+
"durationMs": 61000,
|
|
259
|
+
"errorCode": "none"
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
{
|
|
264
|
+
"dir": "la->cp",
|
|
265
|
+
"frame": {
|
|
266
|
+
"schemaVersion": 2,
|
|
267
|
+
"type": "job.result",
|
|
268
|
+
"msgId": "00000000-0000-4000-8000-000000000003",
|
|
269
|
+
"seq": 3,
|
|
270
|
+
"tenantId": "3f2b8c1e-5a4d-4e7b-9c21-0a1b2c3d4e5f",
|
|
271
|
+
"workspaceId": "ws-1",
|
|
272
|
+
"ts": "2026-10-29T10:00:00Z",
|
|
273
|
+
"agentId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
|
|
274
|
+
"payload": {
|
|
275
|
+
"jobId": "c3d4e5f6-a7b8-4c9d-8e0f-2a3b4c5d6e7f",
|
|
276
|
+
"outcome": "no_change",
|
|
277
|
+
"filesChangedCount": 0
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
"dir": "la->cp",
|
|
283
|
+
"frame": {
|
|
284
|
+
"schemaVersion": 2,
|
|
285
|
+
"type": "content.response",
|
|
286
|
+
"msgId": "00000000-0000-4000-8000-000000000004",
|
|
287
|
+
"seq": 4,
|
|
288
|
+
"tenantId": "3f2b8c1e-5a4d-4e7b-9c21-0a1b2c3d4e5f",
|
|
289
|
+
"workspaceId": "ws-1",
|
|
290
|
+
"ts": "2026-10-29T10:00:00Z",
|
|
291
|
+
"agentId": "a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d",
|
|
292
|
+
"requestId": "e5f6a7b8-c9d0-4e1f-8a2b-4c5d6e7f8091",
|
|
293
|
+
"chunk": {
|
|
294
|
+
"index": 0,
|
|
295
|
+
"total": 1,
|
|
296
|
+
"last": true
|
|
297
|
+
},
|
|
298
|
+
"payload": {
|
|
299
|
+
"status": "ok",
|
|
300
|
+
"contentType": "application/json",
|
|
301
|
+
"encoding": "base64",
|
|
302
|
+
"data": "eyJldmVudHMiOlt7ImkiOjAsImUiOiJkYXRhOiB7XCJ0eXBlXCI6XCJzdGF0dXNcIn0ifSx7ImkiOjEsImUiOiJkYXRhOiB7XCJ0eXBlXCI6XCJzdW1tYXJ5XCJ9In1dLCJuZXh0UmVmIjoiQVFFQkFRRUJBUUVCQVFFQkFRRUJBUUVCQVFFQkFRRUJBUUVCQVFFQkFRRSIsImRvbmUiOnRydWV9",
|
|
303
|
+
"byteLength": 165
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
],
|
|
308
|
+
"expect": {
|
|
309
|
+
"ok": true
|
|
310
|
+
}
|
|
311
|
+
}
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// LA-3 review fix: the event lines of agent2 are JSON; the DLP must judge their DECODED strings, not the serialized line (inside JSON a quoted value is \" and the
|
|
2
|
+
// assignment patterns stopped matching). Unit (maskEventLine), disk (ChatStore) and fuzz of formats. The wire (job.stream body) is in chat-la.test.js.
|
|
3
|
+
import { test } from "node:test";
|
|
4
|
+
import assert from "node:assert/strict";
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import os from "node:os";
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import crypto from "node:crypto";
|
|
9
|
+
import { maskEventLine, WITHHELD_LINE } from "../../src/la-protocol-v2/chat/dlp.js";
|
|
10
|
+
import { resolveChatConfig } from "../../src/la-protocol-v2/chat/config.js";
|
|
11
|
+
import { createChatStore } from "../../src/la-protocol-v2/chat/store.js";
|
|
12
|
+
|
|
13
|
+
const TENANT = "3f2b8c1e-5a4d-4e7b-9c21-0a1b2c3d4e5f";
|
|
14
|
+
const ENV = { LOCAL_AGENT_DLP_MODE: "enforce", LOCAL_AGENT_DLP_BLOCK_CLASSES: "private-key" };
|
|
15
|
+
const PW = "Sup3rS3cretValue!99";
|
|
16
|
+
const KEY = "abcd1234efgh5678ijkl9999";
|
|
17
|
+
const SECRETS = [PW, KEY];
|
|
18
|
+
const mask = (o) => maskEventLine(typeof o === "string" ? o : JSON.stringify(o), WITHHELD_LINE, { env: ENV, source: "t" });
|
|
19
|
+
const leaks = (text) => SECRETS.filter((s) => text.includes(s));
|
|
20
|
+
|
|
21
|
+
const CASES = {
|
|
22
|
+
"object with secret-named keys (the reviewer's case)": { type: "tool_result", content: { db_password: PW, api_key: KEY } },
|
|
23
|
+
"token with a quoted assignment": { type: "token", content: `the config says password = "${PW}" ok` },
|
|
24
|
+
"summary answer with a quoted assignment": { type: "summary", content: { answer: `use password = "${PW}" and api_key: "${KEY}"`, conversationId: "x" } },
|
|
25
|
+
"tool output: a JSON document inside a string": { type: "tool_result", output: JSON.stringify({ db_password: PW, api_key: KEY }) },
|
|
26
|
+
"tool output: env file with single quotes": { type: "tool_result", output: `DB_PASSWORD='${PW}'\nAPI_KEY='${KEY}'\n` },
|
|
27
|
+
"nested quotes and escapes": { type: "token", content: `say \\"password = \\\\\\"${PW}\\\\\\"\\"` },
|
|
28
|
+
"array of objects": { type: "x", items: [{ client_secret: PW }, [{ apiKey: KEY }]] },
|
|
29
|
+
"secret after a line break": { type: "token", content: `line one\r\nline two\npassword = "${PW}"\n\napi_key=\t${KEY}` },
|
|
30
|
+
"unicode escapes written in the raw line": null, // below: the raw line carries \\u escapes
|
|
31
|
+
"key named __proto__": null,
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
for (const [name, ev] of Object.entries(CASES)) {
|
|
35
|
+
if (!ev) continue;
|
|
36
|
+
test(`maskEventLine: ${name} - no secret survives and the line is still JSON`, () => {
|
|
37
|
+
const m = mask(ev);
|
|
38
|
+
assert.equal(m.withheld, false, m.text);
|
|
39
|
+
assert.deepEqual(leaks(m.text), []);
|
|
40
|
+
assert.ok(m.total > 0);
|
|
41
|
+
assert.doesNotThrow(() => JSON.parse(m.text));
|
|
42
|
+
assert.equal(JSON.parse(m.text).type, ev.type, "the structure is kept");
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
test("maskEventLine: the raw line spells the secret with \\u escapes and \\/ : decoded before judging", () => {
|
|
47
|
+
const esc = (s) => [...s].map((c) => `\\u${c.charCodeAt(0).toString(16).padStart(4, "0")}`).join("");
|
|
48
|
+
const raw = `{"type":"token","content":"password \\u003d \\"${PW}\\"","d\\u0062_password":"${esc(PW)}"}`;
|
|
49
|
+
const m = mask(raw);
|
|
50
|
+
assert.equal(m.withheld, false);
|
|
51
|
+
assert.deepEqual(leaks(m.text), []);
|
|
52
|
+
assert.ok(!m.text.includes(PW) && !JSON.parse(m.text).content.includes(PW));
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test("maskEventLine: __proto__ key and a secret used as a KEY are handled, no prototype pollution", () => {
|
|
56
|
+
const raw = `{"type":"x","__proto__":{"db_password":"${PW}"},"sk-ant-${"a1".repeat(15)}":"v"}`;
|
|
57
|
+
const m = mask(raw);
|
|
58
|
+
assert.equal(m.withheld, false);
|
|
59
|
+
assert.deepEqual(leaks(m.text), []);
|
|
60
|
+
assert.ok(!m.text.includes("sk-ant-a1a1"), "a secret in a key is masked as well");
|
|
61
|
+
assert.equal({}.db_password, undefined);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test("maskEventLine: a line that is not JSON is masked as text; a bare JSON scalar too", () => {
|
|
65
|
+
for (const raw of [`not json password = "${PW}"`, JSON.stringify(`password = "${PW}"`), "{\"type\":\"token\",\"content\":\"cut off password = \\\""]) {
|
|
66
|
+
const m = mask(raw);
|
|
67
|
+
assert.deepEqual(leaks(m.text), [], raw);
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
test("maskEventLine: fail closed - DLP not enforce, a blocked class, oversize, too deep -> the whole line is withheld", () => {
|
|
72
|
+
const ok = JSON.stringify({ type: "token", content: "hello" });
|
|
73
|
+
assert.equal(maskEventLine(ok, WITHHELD_LINE, { env: { LOCAL_AGENT_DLP_MODE: "warn" } }).withheld, true);
|
|
74
|
+
assert.equal(maskEventLine(ok, WITHHELD_LINE, { env: {} }).withheld, true);
|
|
75
|
+
const pem = { type: "tool_result", output: "-----BEGIN RSA PRIVATE KEY-----\nMIIEowIBAAKCAQEAxxxxxxxxxxxxxxxx\n-----END RSA PRIVATE KEY-----" };
|
|
76
|
+
const w = mask(pem);
|
|
77
|
+
assert.deepEqual([w.withheld, w.text], [true, WITHHELD_LINE]);
|
|
78
|
+
assert.equal(maskEventLine(ok, WITHHELD_LINE, { env: { ...ENV, LOCAL_AGENT_DLP_MAX_BYTES: "3" } }).withheld, true);
|
|
79
|
+
let deep = "x";
|
|
80
|
+
for (let i = 0; i < 200; i++) deep = { a: deep };
|
|
81
|
+
assert.equal(mask(deep).withheld, true);
|
|
82
|
+
assert.equal(mask("{\"a\":" + "[".repeat(5) + "]".repeat(5) + "}").withheld, false, "shallow nesting is fine");
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
test("maskEventLine: harmless content is untouched (same JSON), a placeholder is not masked twice", () => {
|
|
86
|
+
const ev = { type: "token", content: "total = 42; the password policy is documented in README", n: 3, ok: true, z: null };
|
|
87
|
+
const m = mask(ev);
|
|
88
|
+
assert.deepEqual(JSON.parse(m.text), ev);
|
|
89
|
+
assert.equal(m.total, 0);
|
|
90
|
+
const once = mask({ type: "token", content: `password = "${PW}"` });
|
|
91
|
+
const twice = mask(once.text);
|
|
92
|
+
assert.equal(twice.text, once.text, "idempotent: the egress mask over the stored mask changes nothing");
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
test("fuzz: random wrappers around secrets in strings, keys, arrays and line breaks never leak", () => {
|
|
96
|
+
let seed = 12345;
|
|
97
|
+
const rnd = (n) => { seed = (seed * 1103515245 + 12345) & 0x7fffffff; return seed % n; };
|
|
98
|
+
const names = ["password", "db_password", "api_key", "apiKey", "client_secret", "auth_token", "PASSWD", "secret", "access_token"];
|
|
99
|
+
const quotes = ['"', "'", "", '\\"', "`"];
|
|
100
|
+
const seps = [" = ", ": ", "=", ":", "=>", " : "];
|
|
101
|
+
const pre = ["", "\n", "\r\n", " ", "export ", "{ ", "foo bar\n\n"];
|
|
102
|
+
for (let i = 0; i < 400; i++) {
|
|
103
|
+
const sec = i % 2 ? PW : KEY;
|
|
104
|
+
const q = quotes[rnd(quotes.length)];
|
|
105
|
+
const text = `${pre[rnd(pre.length)]}${names[rnd(names.length)]}${seps[rnd(seps.length)]}${q}${sec}${q}${pre[rnd(pre.length)]}`;
|
|
106
|
+
const shape = rnd(4);
|
|
107
|
+
const ev = shape === 0 ? { type: "token", content: text }
|
|
108
|
+
: shape === 1 ? { type: "tool_result", output: { [names[rnd(names.length)]]: sec, other: [text] } }
|
|
109
|
+
: shape === 2 ? { type: "summary", content: { answer: text } }
|
|
110
|
+
: { type: "tool_result", output: JSON.stringify({ [names[rnd(names.length)]]: sec }) };
|
|
111
|
+
const m = mask(ev);
|
|
112
|
+
assert.deepEqual(leaks(m.text), [], `iteration ${i}: ${JSON.stringify(ev)}`);
|
|
113
|
+
JSON.parse(m.text);
|
|
114
|
+
}
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
function mkStore() {
|
|
118
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), "la-chat-dlpjson-"));
|
|
119
|
+
const env = { ...ENV, LOCAL_AGENT_CHAT: "true", LOCAL_AGENT_CHAT_DIR: dir };
|
|
120
|
+
const config = resolveChatConfig(env, () => {});
|
|
121
|
+
const store = createChatStore({ dir, tenantId: TENANT, config, env, log: { info() {}, warn() {}, log() {} } });
|
|
122
|
+
return { dir, store, stop: () => { store.close(); fs.rmSync(dir, { recursive: true, force: true }); } };
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
test("disk: quoted secrets in a token, a tool result and a summary are not in the .jsonl", async () => {
|
|
126
|
+
const t = mkStore();
|
|
127
|
+
try {
|
|
128
|
+
const id = crypto.randomUUID();
|
|
129
|
+
const rec = t.store.begin({ jobId: id, workspaceId: "ws-1", userText: "hi" });
|
|
130
|
+
rec.append(JSON.stringify({ type: "token", content: `password = "${PW}"` }));
|
|
131
|
+
rec.append(JSON.stringify({ type: "tool_result", content: { db_password: PW, api_key: KEY } }));
|
|
132
|
+
rec.append(JSON.stringify({ type: "tool_result", output: JSON.stringify({ db_password: PW }) }));
|
|
133
|
+
rec.append(JSON.stringify({ type: "summary", content: { answer: `password = "${PW}"`, conversationId: id } }));
|
|
134
|
+
rec.finish("done");
|
|
135
|
+
await t.store.flush();
|
|
136
|
+
const disk = fs.readFileSync(path.join(t.dir, TENANT, `${id}.jsonl`), "utf8");
|
|
137
|
+
assert.deepEqual(leaks(disk), [], "no secret on the disk");
|
|
138
|
+
assert.match(disk, /REDACTED/);
|
|
139
|
+
const r = await t.store.readStream({ conversationId: id, workspaceId: "ws-1", from: 0, waitMs: 0 });
|
|
140
|
+
assert.equal(r.events.length, 4);
|
|
141
|
+
assert.deepEqual(leaks(JSON.stringify(r.events)), []);
|
|
142
|
+
const s = t.store.readSession({ conversationId: id, workspaceId: "ws-1" });
|
|
143
|
+
assert.deepEqual(leaks(JSON.stringify(s)), []);
|
|
144
|
+
} finally { t.stop(); }
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
test("begin on a conversation that already has its end mark does not truncate it: the old file is archived", async () => {
|
|
148
|
+
const t = mkStore();
|
|
149
|
+
try {
|
|
150
|
+
const id = crypto.randomUUID();
|
|
151
|
+
const a = t.store.begin({ jobId: id, workspaceId: "ws-1", userText: "first run" });
|
|
152
|
+
a.append(JSON.stringify({ type: "token", content: "FIRST-RUN-EVENT" }));
|
|
153
|
+
a.finish("done");
|
|
154
|
+
await t.store.flush();
|
|
155
|
+
const file = path.join(t.dir, TENANT, `${id}.jsonl`);
|
|
156
|
+
const before = fs.readFileSync(file, "utf8");
|
|
157
|
+
assert.match(before, /"k":"end"/);
|
|
158
|
+
const b = t.store.begin({ jobId: id, workspaceId: "ws-1", userText: "second run" });
|
|
159
|
+
b.append(JSON.stringify({ type: "token", content: "SECOND-RUN-EVENT" }));
|
|
160
|
+
b.finish("done");
|
|
161
|
+
await t.store.flush();
|
|
162
|
+
const now = fs.readFileSync(file, "utf8");
|
|
163
|
+
assert.ok(!now.includes("FIRST-RUN-EVENT") && now.includes("SECOND-RUN-EVENT"), "the live file is the new run's");
|
|
164
|
+
const archived = fs.readdirSync(path.join(t.dir, TENANT)).filter((n) => n.startsWith(`${id}.`) && n !== `${id}.jsonl`);
|
|
165
|
+
assert.equal(archived.length, 1, "the finished conversation was kept, not truncated");
|
|
166
|
+
assert.equal(fs.readFileSync(path.join(t.dir, TENANT, archived[0]), "utf8"), before);
|
|
167
|
+
assert.equal((fs.statSync(path.join(t.dir, TENANT, archived[0])).mode & 0o777), 0o600);
|
|
168
|
+
} finally { t.stop(); }
|
|
169
|
+
});
|