hanoman 0.1.11 → 0.1.13
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/README.md +27 -0
- package/dist/build-info.json +3 -3
- package/dist/cli.js +22356 -318
- package/dist/server.js +5218 -1933
- package/package.json +1 -1
- package/prisma/migrations/20260801190000_lead_choice/migration.sql +13 -0
- package/prisma/migrations/20260801220000_webhook/migration.sql +48 -0
- package/prisma/migrations/20260801230000_auto_merge/migration.sql +8 -0
- package/prisma/migrations/20260801230000_custom_agent_runtime/migration.sql +8 -0
- package/prisma/migrations/20260801234500_lead_flow/migration.sql +36 -0
- package/prisma/schema.prisma +103 -0
- package/web/assets/{index-D8IVSqJP.js → index-DRwKLKdp.js} +1618 -1557
- package/web/index.html +1 -1
package/package.json
CHANGED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
-- SPEC-480 · ADR-0098 · putusan lead yang bisa dipakai mesin: pilihan tersimpan sebagai data.
|
|
2
|
+
--
|
|
3
|
+
-- Ditulis tangan (bukan `migrate dev`): worktree tetangga membuat `migrate dev` me-reset DB saat
|
|
4
|
+
-- ada drift. ADITIF murni — empat kolom NULLABLE tanpa default, tak ada tabel diredefinisi, tak
|
|
5
|
+
-- ada baris disentuh. Larangan SQLite atas `ADD COLUMN … DEFAULT <non-konstan>` (lihat migration
|
|
6
|
+
-- SPEC-408) tak berlaku di sini karena tak ada default sama sekali.
|
|
7
|
+
--
|
|
8
|
+
-- `options` menyimpan menu yang DIKIRIM PEMINTA. Tanpa itu jejaknya tak bisa dibaca ulang:
|
|
9
|
+
-- `question` tersimpan, opsinya tidak, jadi "lead memilih opsi 2" tak bisa diverifikasi kemudian.
|
|
10
|
+
ALTER TABLE "LeadDecision" ADD COLUMN "choice" TEXT;
|
|
11
|
+
ALTER TABLE "LeadDecision" ADD COLUMN "choiceIndex" INTEGER;
|
|
12
|
+
ALTER TABLE "LeadDecision" ADD COLUMN "options" JSONB;
|
|
13
|
+
ALTER TABLE "LeadDecision" ADD COLUMN "missing" JSONB;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
-- SPEC-481 · ADR-0100 · webhook keluar: endpoint + antrean/riwayat pengiriman.
|
|
2
|
+
--
|
|
3
|
+
-- Dua tabel BARU → `CREATE TABLE` polos; tak ada redefinisi tabel seperti migration SPEC-408.
|
|
4
|
+
-- Keduanya LOCAL-only (tanpa kolom `version`): barisnya memegang secret dan menunjuk pengiriman
|
|
5
|
+
-- dari mesin ini.
|
|
6
|
+
CREATE TABLE "WebhookEndpoint" (
|
|
7
|
+
"id" TEXT NOT NULL PRIMARY KEY,
|
|
8
|
+
"name" TEXT NOT NULL,
|
|
9
|
+
"url" TEXT NOT NULL,
|
|
10
|
+
"secret" TEXT NOT NULL,
|
|
11
|
+
"events" JSONB NOT NULL,
|
|
12
|
+
"projectIds" JSONB,
|
|
13
|
+
"enabled" BOOLEAN NOT NULL DEFAULT true,
|
|
14
|
+
"allowPrivate" BOOLEAN NOT NULL DEFAULT false,
|
|
15
|
+
"apiVersion" INTEGER NOT NULL DEFAULT 1,
|
|
16
|
+
"maxPerMinute" INTEGER NOT NULL DEFAULT 60,
|
|
17
|
+
"disabledAt" DATETIME,
|
|
18
|
+
"disabledReason" TEXT,
|
|
19
|
+
"lastSuccessAt" DATETIME,
|
|
20
|
+
"lastFailureAt" DATETIME,
|
|
21
|
+
"failureStreak" INTEGER NOT NULL DEFAULT 0,
|
|
22
|
+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
23
|
+
"updatedAt" DATETIME NOT NULL
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
CREATE TABLE "WebhookDelivery" (
|
|
27
|
+
"id" TEXT NOT NULL PRIMARY KEY,
|
|
28
|
+
"endpointId" TEXT NOT NULL,
|
|
29
|
+
"eventId" TEXT NOT NULL,
|
|
30
|
+
"eventType" TEXT NOT NULL,
|
|
31
|
+
"projectId" TEXT,
|
|
32
|
+
"payload" JSONB NOT NULL,
|
|
33
|
+
"status" TEXT NOT NULL DEFAULT 'pending',
|
|
34
|
+
"attempt" INTEGER NOT NULL DEFAULT 0,
|
|
35
|
+
"maxAttempts" INTEGER NOT NULL DEFAULT 6,
|
|
36
|
+
"nextAttemptAt" DATETIME,
|
|
37
|
+
"httpStatus" INTEGER,
|
|
38
|
+
"durationMs" INTEGER,
|
|
39
|
+
"error" TEXT,
|
|
40
|
+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
41
|
+
"sentAt" DATETIME,
|
|
42
|
+
CONSTRAINT "WebhookDelivery_endpointId_fkey" FOREIGN KEY ("endpointId")
|
|
43
|
+
REFERENCES "WebhookEndpoint" ("id") ON DELETE CASCADE ON UPDATE CASCADE
|
|
44
|
+
);
|
|
45
|
+
|
|
46
|
+
CREATE INDEX "WebhookDelivery_status_nextAttemptAt_idx" ON "WebhookDelivery" ("status", "nextAttemptAt");
|
|
47
|
+
CREATE INDEX "WebhookDelivery_endpointId_createdAt_idx" ON "WebhookDelivery" ("endpointId", "createdAt");
|
|
48
|
+
CREATE INDEX "WebhookDelivery_eventId_idx" ON "WebhookDelivery" ("eventId");
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
-- SPEC-486 · ADR-0103 · kebijakan auto-merge saat sesi selesai.
|
|
2
|
+
--
|
|
3
|
+
-- ADITIF & nullable tanpa default → `ADD COLUMN` polos (pola migration SPEC-447). Yang dilarang
|
|
4
|
+
-- SQLite adalah `ADD COLUMN … DEFAULT <non-konstan>`, bukan `ADD COLUMN` itu sendiri. Baris lama
|
|
5
|
+
-- tetap NULL, dan NULL berarti "tanpa auto-merge" di Project / "warisi project" di Spec —
|
|
6
|
+
-- jadi tak ada backfill, dan project lama tak berubah perilaku satu langkah git pun.
|
|
7
|
+
ALTER TABLE "Project" ADD COLUMN "autoMerge" JSONB;
|
|
8
|
+
ALTER TABLE "Spec" ADD COLUMN "autoMerge" JSONB;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
-- SPEC-484 · ADR-0101 · penyaring mesin sesi per definisi agen.
|
|
2
|
+
--
|
|
3
|
+
-- NULLABLE TANPA DEFAULT, dan itu yang membuat migration ini satu baris: setiap baris lama tetap
|
|
4
|
+
-- NULL = "ikut sesi induk" = perilaku ADR-0094 apa adanya, jadi tak ada backfill dan tak ada satu
|
|
5
|
+
-- pun roster yang berubah saat rilis ini mendarat. Larangan SQLite atas
|
|
6
|
+
-- `ADD COLUMN … DEFAULT CURRENT_TIMESTAMP` (migration SPEC-408) tak berlaku di sini justru karena
|
|
7
|
+
-- kolomnya tanpa default — tabel tak perlu diredefinisi.
|
|
8
|
+
ALTER TABLE "CustomAgent" ADD COLUMN "runtime" TEXT;
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
-- SPEC-485 · ADR-0102 · rantai keputusan lead + pilihan jamak.
|
|
2
|
+
--
|
|
3
|
+
-- Ditulis tangan (bukan `migrate dev`): worktree tetangga membuat `migrate dev` me-reset DB saat
|
|
4
|
+
-- ada drift. ADITIF murni — satu tabel baru + empat kolom NULLABLE tanpa default, tak ada tabel
|
|
5
|
+
-- diredefinisi, tak ada baris disentuh. Larangan SQLite atas `ADD COLUMN … DEFAULT <non-konstan>`
|
|
6
|
+
-- (lihat migration SPEC-408) tak berlaku di sini karena tak ada default sama sekali pada ALTER.
|
|
7
|
+
--
|
|
8
|
+
-- `LeadFlow` LOCAL-only: tanpa kolom `version`, tak pernah masuk changefeed sync — barisnya
|
|
9
|
+
-- menunjuk sesi tmux & worktree di MESIN INI, persis alasan `LeadDecision` juga tak disync.
|
|
10
|
+
CREATE TABLE "LeadFlow" (
|
|
11
|
+
"id" TEXT NOT NULL PRIMARY KEY,
|
|
12
|
+
"projectId" TEXT NOT NULL,
|
|
13
|
+
"specId" TEXT,
|
|
14
|
+
"sessionId" TEXT,
|
|
15
|
+
"gate" TEXT NOT NULL,
|
|
16
|
+
"status" TEXT NOT NULL DEFAULT 'menunggu',
|
|
17
|
+
"title" TEXT NOT NULL,
|
|
18
|
+
"steps" INTEGER NOT NULL DEFAULT 0,
|
|
19
|
+
"closeReason" TEXT,
|
|
20
|
+
"openedAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
21
|
+
"closedAt" DATETIME,
|
|
22
|
+
"expiresAt" DATETIME NOT NULL,
|
|
23
|
+
"createdAt" DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
|
24
|
+
"updatedAt" DATETIME NOT NULL
|
|
25
|
+
);
|
|
26
|
+
CREATE INDEX "LeadFlow_projectId_createdAt_idx" ON "LeadFlow"("projectId", "createdAt");
|
|
27
|
+
CREATE INDEX "LeadFlow_status_idx" ON "LeadFlow"("status");
|
|
28
|
+
|
|
29
|
+
-- `choices` menggantikan pasangan skalar `choice`/`choiceIndex` sebagai bentuk yang berlaku; yang
|
|
30
|
+
-- lama DIPERTAHANKAN dan diisi dari `choices[0]` supaya pembaca lama (dan baris pra-migrasi) tetap
|
|
31
|
+
-- terbaca tanpa backfill apa pun.
|
|
32
|
+
ALTER TABLE "LeadDecision" ADD COLUMN "flowId" TEXT;
|
|
33
|
+
ALTER TABLE "LeadDecision" ADD COLUMN "step" INTEGER;
|
|
34
|
+
ALTER TABLE "LeadDecision" ADD COLUMN "choices" JSONB;
|
|
35
|
+
ALTER TABLE "LeadDecision" ADD COLUMN "select" JSONB;
|
|
36
|
+
CREATE INDEX "LeadDecision_flowId_idx" ON "LeadDecision"("flowId");
|
package/prisma/schema.prisma
CHANGED
|
@@ -21,6 +21,9 @@ model Project {
|
|
|
21
21
|
helpEnabled Boolean @default(false) // SPEC-253 · opt-in Help Center publik
|
|
22
22
|
schedulerOptIn Boolean @default(false) // SPEC-294 · ADR-0072 · opt-in scheduler otonom (LOCAL — tak masuk FIELDS sync)
|
|
23
23
|
leadOptIn Boolean @default(false) // SPEC-409 · ADR-0091 · opt-in hanoman-lead (LOCAL — cermin schedulerOptIn)
|
|
24
|
+
// SPEC-486 · ADR-0103 · kebijakan auto-merge saat backlog item selesai (LOCAL — tak masuk
|
|
25
|
+
// FIELDS sync, cermin repoDir/schedulerOptIn). null = tanpa auto-merge (default, nol backfill).
|
|
26
|
+
autoMerge Json?
|
|
24
27
|
specs Spec[]
|
|
25
28
|
tickets Ticket[]
|
|
26
29
|
customAgents CustomAgent[] // SPEC-450 · ADR-0094 · custom agent milik project ini
|
|
@@ -54,6 +57,9 @@ model Spec {
|
|
|
54
57
|
// dan kolom ikut FIELDS.spec sync apa adanya. Integritas ditegakkan di boundary route +
|
|
55
58
|
// pembersihan saat spec dihapus, bukan FK.
|
|
56
59
|
dependsOn Json?
|
|
60
|
+
// SPEC-486 · ADR-0103 · override kebijakan auto-merge untuk item ini. null = warisi project.
|
|
61
|
+
// `Json?` dengan alasan yang sama seperti dependsOn: blok dibaca utuh, tak pernah difilter.
|
|
62
|
+
autoMerge Json?
|
|
57
63
|
updatedAt DateTime @updatedAt
|
|
58
64
|
project Project @relation(fields: [projectId], references: [id], onDelete: Cascade)
|
|
59
65
|
}
|
|
@@ -82,6 +88,7 @@ model CustomAgent {
|
|
|
82
88
|
tools Json?
|
|
83
89
|
model String?
|
|
84
90
|
mentions Json?
|
|
91
|
+
runtime String? // SPEC-484 · ADR-0101 · penyaring mesin sesi; null = ikut sesi induk
|
|
85
92
|
enabled Boolean @default(true)
|
|
86
93
|
version Int @default(0) // SPEC-213 · version-stamp sync (ADR-0045)
|
|
87
94
|
createdAt DateTime @default(now())
|
|
@@ -371,6 +378,33 @@ model SchedulerQueueItem {
|
|
|
371
378
|
@@index([status])
|
|
372
379
|
}
|
|
373
380
|
|
|
381
|
+
// SPEC-485 · ADR-0102 · satu RANTAI keputusan lead: beberapa pertanyaan berurutan yang satu urusan,
|
|
382
|
+
// dari pertanyaan pertama sampai submit akhir. Sebelum model ini "alur" hanya ada sebagai kebetulan
|
|
383
|
+
// (baris jejak yang berdekatan waktunya), jadi tak ada tempat untuk menegakkan "pertanyaan lanjutan
|
|
384
|
+
// hanya boleh masuk ke alur yang masih aktif" dan tak ada yang bisa ditanya "sudah di-submit belum".
|
|
385
|
+
//
|
|
386
|
+
// LOCAL-ONLY (tanpa `version`, tak pernah disync) dan TANPA FK — cermin LeadDecision di bawah:
|
|
387
|
+
// id project/spec/sesi boleh lenyap tanpa menyeret jejaknya.
|
|
388
|
+
model LeadFlow {
|
|
389
|
+
id String @id @default(cuid())
|
|
390
|
+
projectId String
|
|
391
|
+
specId String?
|
|
392
|
+
sessionId String?
|
|
393
|
+
gate String // pintu yang membuka rantai (contract|detected|pulse)
|
|
394
|
+
status String @default("menunggu") // menunggu | sebagian | selesai | dibatalkan
|
|
395
|
+
title String // pertanyaan pertama, terpangkas — ini yang dibaca operator
|
|
396
|
+
steps Int @default(0)
|
|
397
|
+
closeReason String? // tunggal | submit | operator | kedaluwarsa
|
|
398
|
+
openedAt DateTime @default(now())
|
|
399
|
+
closedAt DateTime?
|
|
400
|
+
expiresAt DateTime // pagar anti-menggantung; penyapunya menumpang tick lead
|
|
401
|
+
createdAt DateTime @default(now())
|
|
402
|
+
updatedAt DateTime @updatedAt
|
|
403
|
+
|
|
404
|
+
@@index([projectId, createdAt])
|
|
405
|
+
@@index([status])
|
|
406
|
+
}
|
|
407
|
+
|
|
374
408
|
// SPEC-409 · ADR-0091 · jejak keputusan hanoman-lead. Data BARU yang harus bertahan (bukan nilai
|
|
375
409
|
// turunan seperti coverage/PRD, ADR-0011/0018): pertanyaan yang ditanyakan sesi yang sudah mati,
|
|
376
410
|
// dan alasan yang dipakai lead, tak bisa dihitung ulang dari sumber mana pun.
|
|
@@ -396,12 +430,26 @@ model LeadDecision {
|
|
|
396
430
|
weighty Boolean @default(false) // memicu notifikasi (AC-25)
|
|
397
431
|
supersededById String? // baris yang menimpanya (jawaban operator, AC-28)
|
|
398
432
|
actor String @default("lead") // lead | operator — siapa yang mengeluarkan baris ini
|
|
433
|
+
// SPEC-480 · ADR-0098 · pilihan sebagai DATA. Keempatnya nullable tanpa default: baris yang lahir
|
|
434
|
+
// sebelum spec ini sah apa adanya, dan "tak ada opsi" beda dari "opsi kosong".
|
|
435
|
+
choice String? // label opsi terpilih, verbatim; null = tak ada opsi / pilihan ditolak
|
|
436
|
+
choiceIndex Int? // 1-basis, sepasang dengan `choice`
|
|
437
|
+
options Json? // string[] — daftar opsi yang DIKIRIM peminta, supaya jejak bisa diaudit
|
|
438
|
+
missing Json? // string[] — apa yang kurang bila lead menyatakan konteksnya tak cukup
|
|
439
|
+
// SPEC-485 · ADR-0102 · aditif & nullable: baris lama sah apa adanya. `choices` adalah bentuk
|
|
440
|
+
// penyimpanan yang BERLAKU; `choice`/`choiceIndex` di atas tinggal turunan `choices[0]`, dan
|
|
441
|
+
// baris pra-migrasi diturunkan balik jadi `choices` saat dibaca (kompatibilitas mundur).
|
|
442
|
+
flowId String? // rantai tempat langkah ini duduk
|
|
443
|
+
step Int? // urutan langkah dalam rantai, 1-basis
|
|
444
|
+
choices Json? // {index,option}[] — SELALU daftar
|
|
445
|
+
select Json? // {mode,min,max} sebagaimana dikirim peminta
|
|
399
446
|
createdAt DateTime @default(now())
|
|
400
447
|
updatedAt DateTime @updatedAt
|
|
401
448
|
|
|
402
449
|
@@index([projectId, createdAt])
|
|
403
450
|
@@index([specId])
|
|
404
451
|
@@index([sessionId])
|
|
452
|
+
@@index([flowId])
|
|
405
453
|
}
|
|
406
454
|
|
|
407
455
|
// SPEC-215 · ADR-0049 · LOCAL-ONLY: override config runtime (env → DB). Tak pernah disync.
|
|
@@ -564,3 +612,58 @@ model TicketAttachment {
|
|
|
564
612
|
|
|
565
613
|
@@index([ticketId])
|
|
566
614
|
}
|
|
615
|
+
|
|
616
|
+
// SPEC-481 · ADR-0100 · endpoint webhook keluar. LOCAL-only (cermin AgentToken/RuntimeConfig):
|
|
617
|
+
// barisnya memegang secret dan menunjuk pengiriman dari MESIN INI, jadi menyiarkannya ke hub
|
|
618
|
+
// akan mengirim kredensial dan riwayat yang tak berlaku di sana. Tanpa `version`/notifySynced.
|
|
619
|
+
//
|
|
620
|
+
// `secret` disimpan sebagai ciphertext `enc:v1:` (services/secret-box.ts, ADR-0097) dan TAK PERNAH
|
|
621
|
+
// dikembalikan utuh oleh API — hanya empat karakter terakhir sebagai `secretHint`.
|
|
622
|
+
model WebhookEndpoint {
|
|
623
|
+
id String @id @default(cuid())
|
|
624
|
+
name String
|
|
625
|
+
url String
|
|
626
|
+
secret String
|
|
627
|
+
events Json // string[]; ["*"] = semua, "spec.*" = satu keluarga
|
|
628
|
+
projectIds Json? // string[] | null = semua project
|
|
629
|
+
enabled Boolean @default(true)
|
|
630
|
+
allowPrivate Boolean @default(false) // izin EKSPLISIT alamat internal/loopback (anti-SSRF)
|
|
631
|
+
apiVersion Int @default(1) // versi amplop yang diminta penerima
|
|
632
|
+
maxPerMinute Int @default(60)
|
|
633
|
+
disabledAt DateTime?
|
|
634
|
+
disabledReason String?
|
|
635
|
+
lastSuccessAt DateTime?
|
|
636
|
+
lastFailureAt DateTime?
|
|
637
|
+
failureStreak Int @default(0)
|
|
638
|
+
createdAt DateTime @default(now())
|
|
639
|
+
updatedAt DateTime @updatedAt
|
|
640
|
+
deliveries WebhookDelivery[]
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
// SPEC-481 · ADR-0100 · satu tabel merangkap ANTREAN dan RIWAYAT.
|
|
644
|
+
//
|
|
645
|
+
// `payload` disimpan per pengiriman, bukan dirender ulang: retry wajib mengirim byte yang SAMA
|
|
646
|
+
// supaya `id` peristiwa stabil dan idempotensi penerima berlaku, dan riwayat harus memperlihatkan
|
|
647
|
+
// apa yang benar-benar dikirim, bukan keadaan hari ini.
|
|
648
|
+
model WebhookDelivery {
|
|
649
|
+
id String @id @default(cuid())
|
|
650
|
+
endpointId String
|
|
651
|
+
eventId String // SAMA untuk semua endpoint dari satu peristiwa
|
|
652
|
+
eventType String
|
|
653
|
+
projectId String?
|
|
654
|
+
payload Json
|
|
655
|
+
status String @default("pending") // pending | sending | sent | failed | dropped
|
|
656
|
+
attempt Int @default(0)
|
|
657
|
+
maxAttempts Int @default(6)
|
|
658
|
+
nextAttemptAt DateTime?
|
|
659
|
+
httpStatus Int?
|
|
660
|
+
durationMs Int?
|
|
661
|
+
error String?
|
|
662
|
+
createdAt DateTime @default(now())
|
|
663
|
+
sentAt DateTime?
|
|
664
|
+
endpoint WebhookEndpoint @relation(fields: [endpointId], references: [id], onDelete: Cascade)
|
|
665
|
+
|
|
666
|
+
@@index([status, nextAttemptAt])
|
|
667
|
+
@@index([endpointId, createdAt])
|
|
668
|
+
@@index([eventId])
|
|
669
|
+
}
|